diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 0f099897b1..0000000000 --- a/.editorconfig +++ /dev/null @@ -1,10 +0,0 @@ -# editorconfig.org -root = true - -[*] -indent_style = space -indent_size = 2 -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index d357d651ad..0000000000 --- a/.eslintignore +++ /dev/null @@ -1,18 +0,0 @@ -*_compressed*.js -*_uncompressed*.js -/msg/* -/core/css.js -/i18n/* -/tests/jsunit/* -/tests/workspace_svg/* -/tests/blocks/* -/demos/* -/accessible/* -/appengine/* -/shim/* -/dist/* -/gh-pages/* -/webpack.config.js -/build/* - -/github-pages/* diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 9b01970189..0000000000 --- a/.eslintrc +++ /dev/null @@ -1,61 +0,0 @@ -{ - "rules": { - "curly": ["error", "multi-line"], - "eol-last": ["error"], - "indent": [ - "error", 2, # Blockly/Google use 2-space indents - # Blockly/Google uses +4 space indents for line continuations. - { - "SwitchCase": 1, - "MemberExpression": 2, - "ObjectExpression": 1, - "FunctionDeclaration": { - "body": 1, - "parameters": 2 - }, - "FunctionExpression": { - "body": 1, - "parameters": 2 - }, - "CallExpression": { - "arguments": 2 - }, - # Ignore default rules for ternary expressions. - "ignoredNodes": ["ConditionalExpression"] - } - ], - "linebreak-style": ["error", "unix"], - "max-len": ["error", 120, 4], - "no-trailing-spaces": ["error", { "skipBlankLines": true }], - "no-unused-vars": [ - "error", - { - "args": "after-used", - # Ignore vars starting with an underscore. - "varsIgnorePattern": "^_", - # Ignore arguments starting with an underscore. - "argsIgnorePattern": "^_" - } - ], - "no-use-before-define": ["error"], - "quotes": ["off"], # Blockly mixes single and double quotes - "semi": ["error", "always"], - "space-before-function-paren": ["error", "never"], # Blockly doesn't have space before function paren - "space-infix-ops": ["error"], - "strict": ["off"], # Blockly uses 'use strict' in files - "no-cond-assign": ["off"], # Blockly often uses cond-assignment in loops - "no-redeclare": ["off"], # Closure style allows redeclarations - "valid-jsdoc": ["error", {"requireReturn": false}], - "no-console": ["off"], - "no-constant-condition": ["off"] - }, - "env": { - "browser": true, - "es6": true - }, - "globals": { - "Blockly": true, # Blockly global - "goog": true, # goog closure libraries/includes - }, - "extends": "eslint:recommended" -} diff --git a/.github/CODEOWNERS.md b/.github/CODEOWNERS.md deleted file mode 100644 index a905f98fa1..0000000000 --- a/.github/CODEOWNERS.md +++ /dev/null @@ -1 +0,0 @@ -@scratchfoundation/scratch-engineering diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md deleted file mode 100644 index 820910eba2..0000000000 --- a/.github/CONTRIBUTING.md +++ /dev/null @@ -1,66 +0,0 @@ -## Contributing -The development of Scratch is an ongoing process, and we love to have people in the Scratch and open source communities help us along the way. - -### Ways to Help - -* **Documenting bugs** - * If you've identified a bug in Scratch you should first check to see if it's been filed as an issue, if not you can file one. Make sure you follow the issue template. - * It's important that we can consistently reproduce issues. When writing an issue, be sure to follow our [reproduction step guidelines](https://github.com/LLK/scratch-gui/wiki/Writing-good-repro-steps). - * Some issues are marked "Needs Repro". Adding a comment with good reproduction steps to those issues is a great way to help. - * If you don't have an issue in mind already, you can look through the [Bugs & Glitches forum.](https://scratch.mit.edu/discuss/3/) Look for users reporting problems, reproduce the problem yourself, and file new issues following our guidelines. - -* **Fixing bugs** - * You can request to fix a bug in a comment on the issue if you at mention the repo coordinator, who for this repo is @ericrosenbaum. - * If the issue is marked "Help Wanted" you can go ahead and start working on it! - * **We will only accept Pull Requests for bugs that have an issue filed that has a priority label** - * If you're interested in fixing a bug with no issue, file the issue first and wait for it to have a priority added to it. - - * We are not looking for Pull Requests ("PR") for every issue and may deny a PR if it doesn't fit our criteria. - * We are far more likely to accept a PR if it is for an issue marked with Help Wanted. - * We will not accept PRs for issues marked with "Needs Discussion" or "Needs Design." - * Wait until the Repo Coordinator assigns the issue to you before you begin work or submit a PR. - -### Learning Git and Github - -If you want to work on fixing issues, you should be familiar with Git and Github. - -* [Learn Git branching](https://learngitbranching.js.org/) includes an introduction to basic git commands and useful branching features. -* Here's a general introduction to [contributing to an open source project](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github). - -**Important:** we follow the [Github Flow process](https://guides.github.com/introduction/flow/) as our development process. - -### How to Fix Bugs -1. Identify which Github issue you are working on. Leave a comment on the issue to let us (and other contributors) know you're working on it. -2. Make sure you have a fork of this repo (see [Github's forking a repo](https://help.github.com/en/github/getting-started-with-github/fork-a-repo) for details) -3. Switch to the `develop` branch, and pull down the latest changes from upstream -4. Run the code, and reproduce the problem -5. Create your branch from the `develop` branch -6. Make code changes to fix the problem -7. Run `npm test` to make sure that your changes pass our tests -8. Commit your changes -9. Push your branch to your fork -10. Create your pull request - 1. Make sure to follow the template in the PR description - 1. Remember to check the “[Allow edits from maintainers](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/allowing-changes-to-a-pull-request-branch-created-from-a-fork)” box - -When submitting pull requests keep in mind: -* please be patient -- it can take a while to find time to review them -* try to change the least amount of code necessary to fix the bug -* the code can't be radically changed without significant coordination with the Scratch Team, so these types of changes should be avoided -* if you find yourself changing a substantial amount of code or considering radical changes, please ask for clarification -- we may have envisioned a different approach, or underestimated the amount of effort - -### Suggestions -![Block sketch](https://user-images.githubusercontent.com/3431616/77192550-1dcebe00-6ab3-11ea-9606-8ecd8500c958.png) - -Please note: **_we are unlikely to accept PRs with new features that haven't been thought through and discussed as a group_**. - -Why? Because we have a strong belief in the value of keeping things simple for new users. It's been said that the Scratch Team spends about one hour of design discussion for every pixel in Scratch. To learn more about our design philosophy, see [the Scratch Developers page](https://scratch.mit.edu/developers), or [this paper](http://web.media.mit.edu/~mres/papers/Scratch-CACM-final.pdf). - -We welcome suggestions! If you want to suggest a feature, please post in our [suggestions forum](https://scratch.mit.edu/discuss/1/). Your suggestion will be helped if you include a mockup design; this can be simple, even hand-drawn. - -### Other resources -Beyond this repo, there are also some other resources that you might want to take a look at: -* [Community Guidelines](https://github.com/LLK/scratch-www/wiki/Community-Guidelines) (we find it important to maintain a constructive and welcoming community, just like on Scratch) -* [Open Source forum](https://scratch.mit.edu/discuss/49/) on Scratch -* [Suggestions forum](https://scratch.mit.edu/discuss/1/) on Scratch -* [Bugs & Glitches forum](https://scratch.mit.edu/discuss/3/) on Scratch diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 38756937c5..0000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,15 +0,0 @@ -### Expected Behavior - -_Please describe what should happen_ - -### Actual Behavior - -_Describe what actually happens_ - -### Steps to Reproduce - -_Explain what someone needs to do in order to see what's described in *Actual behavior* above_ - -### Operating System and Browser - -_e.g. Mac OS 10.11.6 Safari 10.0_ diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 33ff6dfdee..0000000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,15 +0,0 @@ -### Resolves - -_What Github issue does this resolve (please include link)?_ - -### Proposed Changes - -_Describe what this Pull Request does_ - -### Reason for Changes - -_Explain why these changes should be made_ - -### Test Coverage - -_Please show how you have added tests to cover your changes_ diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml deleted file mode 100644 index a6609e4f2d..0000000000 --- a/.github/workflows/commitlint.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: Lint commit messages -on: [pull_request] - -concurrency: - group: "${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.sha }}" - -jobs: - commitlint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 - - uses: wagoid/commitlint-github-action@5ce82f5d814d4010519d15f0552aec4f17a1e1fe # v5 diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index cd39592905..0000000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: build-scratch-blocks -on: - push: # Runs whenever a commit is pushed to the repository - workflow_dispatch: # Allows you to run this workflow manually from the Actions tab -jobs: - setup: - runs-on: ubuntu-latest - env: - JVM_OPTS: -Xmx3200m - PROJECT_PATH: ./scratch-blocks - steps: - - run: | - for F in chrome chromium chromedriver; do - which $F && $F --version || echo Not found: $F - done - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3 - - name: Check Python version - run: python --version - - name: Setup Java - uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3 - with: - distribution: 'temurin' - java-version: 17 - - name: Setup Node - uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3 - with: - node-version-file: '.nvmrc' - - name: Install Node Dependencies - run: npm ci - - name: Lint - run: npm run test:lint - - name: Run Tests - run: npm run test:messages - - name: Run Unit Tests - run: DISPLAY=:99 npm run test:unit - - name: Remove Closure App - run: rm -rf gh-pages/closure-library/scripts/ci/CloseAdobeDialog.exe - - name: Deploy playground to GitHub Pages - uses: peaceiris/actions-gh-pages@373f7f263a76c20808c831209c920827a82a2847 # v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./gh-pages - full_commit_message: "Build for ${{ github.sha }} ${{ github.event.head_commit.message }}" - enable_jekyll: true - - name: Run semantic-release - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: npx --no -- semantic-release diff --git a/.github/workflows/signature-assistant.yml b/.github/workflows/signature-assistant.yml deleted file mode 100644 index 4820e949ab..0000000000 --- a/.github/workflows/signature-assistant.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: "Signature Assistant" -on: - issue_comment: - types: [created] - pull_request_target: - types: [opened, closed, synchronize] - -permissions: - actions: write - contents: read - pull-requests: write - statuses: write - -jobs: - CLA-Assistant: - if: github.event_name == 'pull_request_target' || - ( - github.event.comment.body == 'recheck' || - github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA' - ) - runs-on: ubuntu-latest - steps: - - uses: scratchfoundation/scratch-agreements/.github/actions/cla-allowlist@main - id: cla-allowlist - - name: "CLA Assistant" - uses: contributor-assistant/github-action@ca4a40a7d1004f18d9960b404b97e5f30a505a08 # v2.6.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # the below token should have repo scope and must be manually added by you in the repository's secrets - PERSONAL_ACCESS_TOKEN: ${{ secrets.GHA_AGREEMENTS_PAT }} - with: - remote-organization-name: "scratchfoundation" - remote-repository-name: "scratch-agreements" - path-to-signatures: "signatures/version1/cla.json" - path-to-document: "https://github.com/scratchfoundation/scratch-agreements/blob/main/CLA.md" - branch: "main" - allowlist: ${{ steps.cla-allowlist.outputs.allowlist }} diff --git a/.gitignore b/.gitignore deleted file mode 100644 index ca5e13595f..0000000000 --- a/.gitignore +++ /dev/null @@ -1,46 +0,0 @@ -# OSX -.DS_Store - -# NPM -/node_modules -npm-* - -# Localization / I18N -common.pyc -.settings -.project -*.pyc -*.komodoproject -/nbproject/private/ - -# Unused by scratch-blocks -dart_compressed.js -javascript_compressed.js -lua_compressed.js -php_compressed.js -python_compressed.js - -# Editor -.vscode - -/accessible/* -/dist -/msg/js/* -!/msg/js/en.js -/msg/json/* -!/msg/json/en.json -/blockly_compressed_horizontal.js -/blockly_compressed_vertical.js -/blockly_uncompressed_horizontal.js -/blockly_uncompressed_vertical.js -/blocks_compressed_horizontal.js -/blocks_compressed_vertical.js -/blocks_compressed.js -/gh-pages/main.js -/gh-pages/playgrounds -/gh-pages/Gemfile.lock -/gh-pages/closure-library -/gh-pages/_site -/*compiler*.jar -/local_blockly_compressed_vertical.js -/chromedriver diff --git a/.husky/.gitattributes b/.husky/.gitattributes deleted file mode 100644 index fcadb2cf97..0000000000 --- a/.husky/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -* text eol=lf diff --git a/.husky/commit-msg b/.husky/commit-msg deleted file mode 100755 index 80416c7b17..0000000000 --- a/.husky/commit-msg +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - -npx --no-install commitlint --edit "$1" diff --git a/.npmignore b/.npmignore deleted file mode 100644 index 89433f5d7a..0000000000 --- a/.npmignore +++ /dev/null @@ -1,26 +0,0 @@ -# Development files -/.circleci -.eslintrc -/.editorconfig -/.eslintignore -/.gitattributes -/.github -/.tx -/tests -/webpack.config.js - -# Localization / I18N -common.pyc -.settings -.project -*.pyc -*.komodoproject -/nbproject/private/ - -/accessible/* - -# Build created files -/gh-pages - -# Exclude already built packages from testing with npm pack -/scratch-blocks-*.{tar,tgz} diff --git a/.npmrc b/.npmrc deleted file mode 100644 index 214c29d139..0000000000 --- a/.npmrc +++ /dev/null @@ -1 +0,0 @@ -registry=https://registry.npmjs.org/ diff --git a/.nvmrc b/.nvmrc deleted file mode 100644 index 3f430af82b..0000000000 --- a/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -v18 diff --git a/.tx/config b/.tx/config deleted file mode 100644 index fcff4d3b53..0000000000 --- a/.tx/config +++ /dev/null @@ -1,9 +0,0 @@ -[main] -host = https://www.transifex.com -lang_map = zh_CN:zh-cn, zh_TW:zh-tw, pt_BR:pt-br, es_419:es-419 - -[scratch-editor.blocks] -file_filter = msg/json/.json -source_file = msg/json/en.json -source_lang = en -type = KEYVALUEJSON diff --git a/gh-pages/Gemfile b/Gemfile similarity index 100% rename from gh-pages/Gemfile rename to Gemfile diff --git a/README.md b/README.md deleted file mode 100644 index 60e1faf935..0000000000 --- a/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# scratch-blocks - -Scratch Blocks is a library for building creative computing interfaces. - -[![CircleCI](https://dl.circleci.com/status-badge/img/gh/LLK/scratch-blocks/tree/develop.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/LLK/scratch-blocks/tree/develop) - -![An image of Scratch Blocks running on a tablet](https://cloud.githubusercontent.com/assets/747641/15227351/c37c09da-1854-11e6-8dc7-9a298f2b1f01.jpg) - -## Introduction - -Scratch Blocks is a fork of Google's [Blockly](https://github.com/google/blockly) project that provides a design -specification and codebase for building creative computing interfaces. Together with the [Scratch Virtual Machine -(VM)](https://github.com/scratchfoundation/scratch-vm) this codebase allows for the rapid design and development of visual -programming interfaces. Unlike [Blockly](https://github.com/google/blockly), Scratch Blocks does not use [code -generators](https://developers.google.com/blockly/guides/configure/web/code-generators), but rather leverages the -[Scratch Virtual Machine](https://github.com/scratchfoundation/scratch-vm) to create highly dynamic, interactive programming -environments. - -*This project is in active development and should be considered a "developer preview" at this time.* - -## Two Types of Blocks - -![A divided image showing horizontal blocks on the left and vertical blocks on the right](https://cloud.githubusercontent.com/assets/747641/15255731/dad4d028-190b-11e6-9c16-8df7445adc96.png) - -Scratch Blocks brings together two different programming "grammars" that the Scratch Team has designed and continued -to refine over the past decade. The standard [Scratch](https://scratch.mit.edu) grammar uses blocks that snap together -vertically, much like LEGO bricks. For our [ScratchJr](https://scratchjr.org) software, intended for younger children, -we developed blocks that are labelled with icons rather than words, and snap together horizontally rather than -vertically. We have found that the horizontal grammar is not only friendlier for beginning programmers but also better -suited for devices with small screens. - -## Documentation - -The "getting started" guide including [FAQ](https://scratch.mit.edu/developers#faq) and [design -documentation](https://github.com/scratchfoundation/scratch-blocks/wiki/Design) can be found in the -[wiki](https://github.com/scratchfoundation/scratch-blocks/wiki). - -## Donate - -We provide [Scratch](https://scratch.mit.edu) free of charge, and want to keep it that way! Please consider making a -[donation](https://secure.donationpay.org/scratchfoundation/) to support our continued engineering, design, community, -and resource development efforts. Donations of any size are appreciated. Thank you! - -## Committing - -This project uses [semantic release](https://github.com/semantic-release/semantic-release) to ensure version bumps -follow semver so that projects depending on it don't break unexpectedly. - -In order to automatically determine version updates, semantic release expects commit messages to follow the -[conventional-changelog](https://github.com/bcoe/conventional-changelog-standard/blob/master/convention.md) -specification. - -You can use the [commitizen CLI](https://github.com/commitizen/cz-cli) to make commits formatted in this way: - -```bash -npm install -g commitizen@latest cz-conventional-changelog@latest -``` - -Now you're ready to make commits using `git cz`. diff --git a/TRADEMARK b/TRADEMARK deleted file mode 100644 index 17b5d4c919..0000000000 --- a/TRADEMARK +++ /dev/null @@ -1 +0,0 @@ -The Scratch trademarks, including the Scratch name, logo, the Scratch Cat, Gobo, Pico, Nano, Tera and Giga graphics (the "Marks"), are property of the Massachusetts Institute of Technology (MIT). Marks may not be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/gh-pages/_config.yml b/_config.yml similarity index 100% rename from gh-pages/_config.yml rename to _config.yml diff --git a/build.py b/build.py deleted file mode 100755 index 3fc56e5fd3..0000000000 --- a/build.py +++ /dev/null @@ -1,636 +0,0 @@ -#!/usr/bin/python2.7 -# Compresses the core Blockly files into a single JavaScript file. -# -# Copyright 2012 Google Inc. -# https://developers.google.com/blockly/ -# -# 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. - -# This script generates two versions of Blockly's core files: -# blockly_compressed.js -# blockly_uncompressed.js -# The compressed file is a concatenation of all of Blockly's core files which -# have been run through Google's Closure Compiler. This is done using the -# online API (which takes a few seconds and requires an Internet connection). -# The uncompressed file is a script that loads in each of Blockly's core files -# one by one. This takes much longer for a browser to load, but is useful -# when debugging code since line numbers are meaningful and variables haven't -# been renamed. The uncompressed file also allows for a faster developement -# cycle since there is no need to rebuild or recompile, just reload. -# -# This script also generates: -# blocks_compressed.js: The compressed common blocks. -# blocks_horizontal_compressed.js: The compressed Scratch horizontal blocks. -# blocks_vertical_compressed.js: The compressed Scratch vertical blocks. -# msg/js/.js for every language defined in msg/js/.json. - -import sys - -import errno, glob, json, os, re, subprocess, threading, codecs, functools - -if sys.version_info[0] == 2: - import httplib - from urllib import urlencode -else: - import http.client as httplib - from urllib.parse import urlencode - from importlib import reload - -REMOTE_COMPILER = "remote" - -CLOSURE_DIR = os.path.pardir -CLOSURE_ROOT = os.path.pardir -CLOSURE_LIBRARY = "closure-library" -CLOSURE_COMPILER = REMOTE_COMPILER - -CLOSURE_DIR_NPM = "node_modules" -CLOSURE_ROOT_NPM = os.path.join("node_modules") -CLOSURE_LIBRARY_NPM = "google-closure-library" -CLOSURE_COMPILER_NPM = ("google-closure-compiler.cmd" if os.name == "nt" else "google-closure-compiler") - -def import_path(fullpath): - """Import a file with full path specification. - Allows one to import from any directory, something __import__ does not do. - - Args: - fullpath: Path and filename of import. - - Returns: - An imported module. - """ - path, filename = os.path.split(fullpath) - filename, ext = os.path.splitext(filename) - sys.path.append(path) - module = __import__(filename) - reload(module) # Might be out of date. - del sys.path[-1] - return module - -def read(filename): - f = open(filename) - content = "".join(f.readlines()) - f.close() - return content - -HEADER = ("// Do not edit this file; automatically generated by build.py.\n" - "'use strict';\n") - - -class Gen_uncompressed(threading.Thread): - """Generate a JavaScript file that loads Blockly's raw files. - Runs in a separate thread. - """ - def __init__(self, search_paths, vertical, closure_env): - threading.Thread.__init__(self) - self.search_paths = search_paths - self.vertical = vertical - self.closure_env = closure_env - - def run(self): - if self.vertical: - target_filename = 'blockly_uncompressed_vertical.js' - else: - target_filename = 'blockly_uncompressed_horizontal.js' - f = open(target_filename, 'w') - f.write(HEADER) - f.write(self.format_js(""" -var isNodeJS = !!(typeof module !== 'undefined' && module.exports && - typeof window === 'undefined'); - -if (isNodeJS) { - var window = {}; - require('{closure_library}'); -} - -window.BLOCKLY_DIR = (function() { - if (!isNodeJS) { - // Find name of current directory. - var scripts = document.getElementsByTagName('script'); - var re = new RegExp('(.+)[\/]blockly_uncompressed(_vertical|_horizontal|)\.js$'); - for (var i = 0, script; script = scripts[i]; i++) { - var match = re.exec(script.src); - if (match) { - return match[1]; - } - } - alert('Could not detect Blockly\\'s directory name.'); - } - return ''; -})(); - -window.BLOCKLY_BOOT = function() { - var dir = ''; - if (isNodeJS) { - require('{closure_library}'); - dir = 'blockly'; - } else { - // Execute after Closure has loaded. - if (!window.goog) { - alert('Error: Closure not found. Read this:\\n' + - 'developers.google.com/blockly/guides/modify/web/closure'); - } - if (window.BLOCKLY_DIR.search(/node_modules/)) { - dir = '..'; - } else { - dir = window.BLOCKLY_DIR.match(/[^\\/]+$/)[0]; - } - } -""")) - add_dependency = [] - base_path = calcdeps.FindClosureBasePath(self.search_paths) - for dep in calcdeps.BuildDependenciesFromFiles(self.search_paths): - add_dependency.append(calcdeps.GetDepsLine(dep, base_path)) - add_dependency.sort() # Deterministic build. - add_dependency = '\n'.join(add_dependency) - # Find the Blockly directory name and replace it with a JS variable. - # This allows blockly_uncompressed.js to be compiled on one computer and be - # used on another, even if the directory name differs. - m = re.search('[\\/]([^\\/]+)[\\/]core[\\/]blockly.js', add_dependency) - add_dependency = re.sub('([\\/])' + re.escape(m.group(1)) + - '([\\/]core[\\/])', '\\1" + dir + "\\2', add_dependency) - f.write(add_dependency + '\n') - - provides = [] - for dep in calcdeps.BuildDependenciesFromFiles(self.search_paths): - # starts with '../' or 'node_modules/' - if not dep.filename.startswith(self.closure_env["closure_root"] + os.sep): - provides.extend(dep.provides) - provides.sort() # Deterministic build. - f.write('\n') - f.write('// Load Blockly.\n') - for provide in provides: - f.write("goog.require('%s');\n" % provide) - - f.write(self.format_js(""" -delete this.BLOCKLY_DIR; -delete this.BLOCKLY_BOOT; -}; - -if (isNodeJS) { - window.BLOCKLY_BOOT(); - module.exports = Blockly; -} else { - // Delete any existing Closure (e.g. Soy's nogoog_shim). - document.write(''); - // Load fresh Closure Library. - document.write(''); - document.write(''); -} -""")) - f.close() - print("SUCCESS: " + target_filename) - - def format_js(self, code): - """Format JS in a way that python's format method can work with to not - consider brace-wrapped sections to be format replacements while still - replacing known keys. - """ - - key_whitelist = self.closure_env.keys() - - keys_pipe_separated = functools.reduce(lambda accum, key: accum + "|" + key, key_whitelist) - begin_brace = re.compile(r"\{(?!%s)" % (keys_pipe_separated,)) - - end_brace = re.compile(r"\}") - def end_replacement(match): - try: - maybe_key = match.string[match.string[:match.start()].rindex("{") + 1:match.start()] - except ValueError: - return "}}" - - if maybe_key and maybe_key in key_whitelist: - return "}" - else: - return "}}" - - return begin_brace.sub("{{", end_brace.sub(end_replacement, code)).format(**self.closure_env) - -class Gen_compressed(threading.Thread): - """Generate a JavaScript file that contains all of Blockly's core and all - required parts of Closure, compiled together. - Uses the Closure Compiler's online API. - Runs in a separate thread. - """ - def __init__(self, search_paths_vertical, search_paths_horizontal, closure_env): - threading.Thread.__init__(self) - self.search_paths_vertical = search_paths_vertical - self.search_paths_horizontal = search_paths_horizontal - self.closure_env = closure_env - - def run(self): - self.gen_core(True) - self.gen_core(False) - self.gen_blocks("horizontal") - self.gen_blocks("vertical") - self.gen_blocks("common") - - def gen_core(self, vertical): - if vertical: - target_filename = 'blockly_compressed_vertical.js' - search_paths = self.search_paths_vertical - else: - target_filename = 'blockly_compressed_horizontal.js' - search_paths = self.search_paths_horizontal - # Define the parameters for the POST request. - params = [ - ("compilation_level", "SIMPLE"), - - # remote will filter this out - ("language_in", "ECMASCRIPT_2017"), - ("language_out", "ECMASCRIPT5"), - ("rewrite_polyfills", "false"), - ("define", "goog.DEBUG=false"), - - # local will filter this out - ("use_closure_library", "true"), - ] - - # Read in all the source files. - filenames = calcdeps.CalculateDependencies(search_paths, - [os.path.join("core", "blockly.js")]) - filenames.sort() # Deterministic build. - for filename in filenames: - # Append filenames as false arguments the step before compiling will - # either transform them into arguments for local or remote compilation - params.append(("js_file", filename)) - - self.do_compile(params, target_filename, filenames, "") - - def gen_blocks(self, block_type): - if block_type == "horizontal": - target_filename = "blocks_compressed_horizontal.js" - filenames = glob.glob(os.path.join("blocks_horizontal", "*.js")) - elif block_type == "vertical": - target_filename = "blocks_compressed_vertical.js" - filenames = glob.glob(os.path.join("blocks_vertical", "*.js")) - elif block_type == "common": - target_filename = "blocks_compressed.js" - filenames = glob.glob(os.path.join("blocks_common", "*.js")) - - # glob.glob ordering is platform-dependent and not necessary deterministic - filenames.sort() # Deterministic build. - - # Define the parameters for the POST request. - params = [ - ("compilation_level", "SIMPLE"), - ] - - # Read in all the source files. - # Add Blockly.Blocks to be compatible with the compiler. - params.append(("js_file", os.path.join("build", "gen_blocks.js"))) - # Add Blockly.Colours for use of centralized colour bank - filenames.append(os.path.join("core", "colours.js")) - filenames.append(os.path.join("core", "constants.js")) - - for filename in filenames: - # Append filenames as false arguments the step before compiling will - # either transform them into arguments for local or remote compilation - params.append(("js_file", filename)) - - # Remove Blockly.Blocks to be compatible with Blockly. - remove = "var Blockly={Blocks:{}};" - self.do_compile(params, target_filename, filenames, remove) - - def do_compile(self, params, target_filename, filenames, remove): - if self.closure_env["closure_compiler"] == REMOTE_COMPILER: - do_compile = self.do_compile_remote - else: - do_compile = self.do_compile_local - json_data = do_compile(params, target_filename) - - if self.report_errors(target_filename, filenames, json_data): - self.write_output(target_filename, remove, json_data) - self.report_stats(target_filename, json_data) - - def do_compile_local(self, params, target_filename): - filter_keys = ["use_closure_library"] - - # Drop arg if arg is js_file else add dashes - dash_params = [] - for (arg, value) in params: - dash_params.append((value,) if arg == "js_file" else ("--" + arg, value)) - - # Flatten dash_params into dash_args if their keys are not in filter_keys - dash_args = [] - for pair in dash_params: - if pair[0][2:] not in filter_keys: - dash_args.extend(pair) - - # Build the final args array by prepending CLOSURE_COMPILER_NPM to - # dash_args and dropping any falsy members - args = [] - for group in [[CLOSURE_COMPILER_NPM], dash_args]: - args.extend(filter(lambda item: item, group)) - - proc = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) - (stdout, stderr) = proc.communicate() - - # Build the JSON response. - filesizes = [os.path.getsize(value) for (arg, value) in params if arg == "js_file"] - return dict( - compiledCode=stdout, - statistics=dict( - originalSize=functools.reduce(lambda v, size: v + size, filesizes, 0), - compressedSize=len(stdout), - ) - ) - - def do_compile_remote(self, params, target_filename): - filter_keys = [ - "language_in", - "language_out", - "rewrite_polyfills", - "define", - ] - - params.extend([ - ("output_format", "json"), - ("output_info", "compiled_code"), - ("output_info", "warnings"), - ("output_info", "errors"), - ("output_info", "statistics"), - ]) - - # Send the request to Google. - remoteParams = [] - for (arg, value) in params: - if not arg in filter_keys: - if arg == "js_file": - if not value.startswith(self.closure_env["closure_root"] + os.sep): - remoteParams.append(("js_code", read(value))) - # Change the normal compilation_level value SIMPLE to the remove - # service's SIMPLE_OPTIMIZATIONS - elif arg == "compilation_level" and value == "SIMPLE": - remoteParams.append((arg, "SIMPLE_OPTIMIZATIONS")) - else: - remoteParams.append((arg, value)) - - headers = {"Content-type": "application/x-www-form-urlencoded"} - conn = httplib.HTTPSConnection("closure-compiler.appspot.com") - conn.request("POST", "/compile", urlencode(remoteParams), headers) - response = conn.getresponse() - # Decode is necessary for Python 3.4 compatibility - json_str = response.read().decode("utf-8") - conn.close() - - # Parse the JSON response. - return json.loads(json_str) - - def report_errors(self, target_filename, filenames, json_data): - def file_lookup(name): - if not name.startswith("Input_"): - return "???" - n = int(name[6:]) - 1 - return filenames[n] - - if "serverErrors" in json_data: - errors = json_data["serverErrors"] - for error in errors: - print("SERVER ERROR: %s" % target_filename) - print(error["error"]) - elif "errors" in json_data: - errors = json_data["errors"] - for error in errors: - print("FATAL ERROR") - print(error["error"]) - if error["file"]: - print("%s at line %d:" % ( - file_lookup(error["file"]), error["lineno"])) - print(error["line"]) - print((" " * error["charno"]) + "^") - sys.exit(1) - else: - if "warnings" in json_data: - warnings = json_data["warnings"] - for warning in warnings: - print("WARNING") - print(warning["warning"]) - if warning["file"]: - print("%s at line %d:" % ( - file_lookup(warning["file"]), warning["lineno"])) - print(warning["line"]) - print((" " * warning["charno"]) + "^") - print() - - return True - - return False - - def write_output(self, target_filename, remove, json_data): - if "compiledCode" not in json_data: - print("FATAL ERROR: Compiler did not return compiledCode.") - sys.exit(1) - - code = HEADER + "\n" + json_data["compiledCode"].decode("utf-8") - code = code.replace(remove, "") - - # Trim down Google's (and only Google's) Apache licences. - # The Closure Compiler preserves these. - LICENSE = re.compile("""/\\* - - [\w ]+ - - Copyright \\d+ Google Inc. - https://developers.google.com/blockly/ - - 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. -\\*/""") - code = re.sub(LICENSE, "", code) - - stats = json_data["statistics"] - original_b = stats["originalSize"] - compressed_b = stats["compressedSize"] - if original_b > 0 and compressed_b > 0: - f = open(target_filename, "w") - f.write(code) - f.close() - - def report_stats(self, target_filename, json_data): - stats = json_data["statistics"] - original_b = stats["originalSize"] - compressed_b = stats["compressedSize"] - if original_b > 0 and compressed_b > 0: - original_kb = int(original_b / 1024 + 0.5) - compressed_kb = int(compressed_b / 1024 + 0.5) - ratio = int(float(compressed_b) / float(original_b) * 100 + 0.5) - print("SUCCESS: " + target_filename) - print("Size changed from %d KB to %d KB (%d%%)." % ( - original_kb, compressed_kb, ratio)) - else: - print("UNKNOWN ERROR") - - -class Gen_langfiles(threading.Thread): - """Generate JavaScript file for each natural language supported. - - Runs in a separate thread. - """ - - def __init__(self): - threading.Thread.__init__(self) - - def _rebuild(self, srcs, dests): - # Determine whether any of the files in srcs is newer than any in dests. - try: - return (max(os.path.getmtime(src) for src in srcs) > - min(os.path.getmtime(dest) for dest in dests)) - except OSError as e: - # Was a file not found? - if e.errno == errno.ENOENT: - # If it was a source file, we can't proceed. - if e.filename in srcs: - print("Source file missing: " + e.filename) - sys.exit(1) - else: - # If a destination file was missing, rebuild. - return True - else: - print("Error checking file creation times: " + str(e)) - - def run(self): - # The files msg/json/{en,qqq,synonyms}.json depend on msg/messages.js. - if self._rebuild([os.path.join("msg", "messages.js")], - [os.path.join("msg", "json", f) for f in - ["en.json", "qqq.json", "synonyms.json"]]): - try: - subprocess.check_call([ - "python", - os.path.join("i18n", "js_to_json.py"), - "--input_file", "msg/messages.js", - "--output_dir", "msg/json/", - "--quiet"]) - except (subprocess.CalledProcessError, OSError) as e: - # Documentation for subprocess.check_call says that CalledProcessError - # will be raised on failure, but I found that OSError is also possible. - print("Error running i18n/js_to_json.py: ", e) - sys.exit(1) - - # Checking whether it is necessary to rebuild the js files would be a lot of - # work since we would have to compare each .json file with each - # .js file. Rebuilding is easy and cheap, so just go ahead and do it. - try: - # Use create_messages.py to create .js files from .json files. - cmd = [ - "python", - os.path.join("i18n", "create_messages.py"), - "--source_lang_file", os.path.join("msg", "json", "en.json"), - "--source_synonym_file", os.path.join("msg", "json", "synonyms.json"), - "--source_constants_file", os.path.join("msg", "json", "constants.json"), - "--key_file", os.path.join("msg", "json", "keys.json"), - "--output_dir", os.path.join("msg", "js"), - "--quiet"] - json_files = glob.glob(os.path.join("msg", "json", "*.json")) - json_files = [file for file in json_files if not - (file.endswith(("keys.json", "synonyms.json", "qqq.json", "constants.json")))] - cmd.extend(json_files) - subprocess.check_call(cmd) - except (subprocess.CalledProcessError, OSError) as e: - print("Error running i18n/create_messages.py: ", e) - sys.exit(1) - - # Output list of .js files created. - for f in json_files: - # This assumes the path to the current directory does not contain "json". - f = f.replace("json", "js") - if os.path.isfile(f): - print("SUCCESS: " + f) - else: - print("FAILED to create " + f) - -def exclude_vertical(item): - return not item.endswith("block_render_svg_vertical.js") - -def exclude_horizontal(item): - return not item.endswith("block_render_svg_horizontal.js") - -if __name__ == "__main__": - try: - closure_dir = CLOSURE_DIR_NPM - closure_root = CLOSURE_ROOT_NPM - closure_library = CLOSURE_LIBRARY_NPM - closure_compiler = CLOSURE_COMPILER_NPM - - # Load calcdeps from the local library - calcdeps = import_path(os.path.join( - closure_root, closure_library, "closure", "bin", "calcdeps.py")) - - # Sanity check the local compiler - test_args = [closure_compiler, os.path.join("build", "test_input.js")] - test_proc = subprocess.Popen(test_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) - (stdout, _) = test_proc.communicate() - assert stdout.decode("utf-8") == read(os.path.join("build", "test_expect.js")) - - print("Using local compiler: %s ...\n" % CLOSURE_COMPILER_NPM) - except (ImportError, AssertionError): - print("Using remote compiler: closure-compiler.appspot.com ...\n") - - try: - closure_dir = CLOSURE_DIR - closure_root = CLOSURE_ROOT - closure_library = CLOSURE_LIBRARY - closure_compiler = CLOSURE_COMPILER - - calcdeps = import_path(os.path.join( - closure_root, closure_library, "closure", "bin", "calcdeps.py")) - except ImportError: - if os.path.isdir(os.path.join(os.path.pardir, "closure-library-read-only")): - # Dir got renamed when Closure moved from Google Code to GitHub in 2014. - print("Error: Closure directory needs to be renamed from" - "'closure-library-read-only' to 'closure-library'.\n" - "Please rename this directory.") - elif os.path.isdir(os.path.join(os.path.pardir, "google-closure-library")): - print("Error: Closure directory needs to be renamed from" - "'google-closure-library' to 'closure-library'.\n" - "Please rename this directory.") - else: - print("""Error: Closure not found. Read this: - developers.google.com/blockly/guides/modify/web/closure""") - sys.exit(1) - - search_paths = list(calcdeps.ExpandDirectories( - ["core", os.path.join(closure_root, closure_library)])) - - search_paths_horizontal = list(filter(exclude_vertical, search_paths)) - search_paths_vertical = list(filter(exclude_horizontal, search_paths)) - - closure_env = { - "closure_dir": closure_dir, - "closure_root": closure_root, - "closure_library": closure_library, - "closure_compiler": closure_compiler, - } - - # Run all tasks in parallel threads. - # Uncompressed is limited by processor speed. - # Compressed is limited by network and server speed. - # Vertical: - Gen_uncompressed(search_paths_vertical, True, closure_env).start() - # Horizontal: - Gen_uncompressed(search_paths_horizontal, False, closure_env).start() - - # Compressed forms of vertical and horizontal. - Gen_compressed(search_paths_vertical, search_paths_horizontal, closure_env).start() - - # This is run locally in a separate thread. - # Gen_langfiles().start() diff --git a/build/gen_blocks.js b/build/gen_blocks.js deleted file mode 100644 index 2ab6e688b5..0000000000 --- a/build/gen_blocks.js +++ /dev/null @@ -1 +0,0 @@ -goog.provide('Blockly.Blocks'); diff --git a/build/test_expect.js b/build/test_expect.js deleted file mode 100644 index 555c3f7c54..0000000000 --- a/build/test_expect.js +++ /dev/null @@ -1 +0,0 @@ -var Blockly={Blocks:{}}; diff --git a/build/test_input.js b/build/test_input.js deleted file mode 100644 index 2ab6e688b5..0000000000 --- a/build/test_input.js +++ /dev/null @@ -1 +0,0 @@ -goog.provide('Blockly.Blocks'); diff --git a/cleanup.sh b/cleanup.sh deleted file mode 100755 index 8af8cf6a70..0000000000 --- a/cleanup.sh +++ /dev/null @@ -1,101 +0,0 @@ -#!/bin/bash - -# Script for cleaning up blockly-specific files when merging blockly into scratch-blocks -# Removes files and directories that scratch-blocks doesn't want. -# Rachel Fenichel (fenichel@google.com) - -# Note: 'ours' is scratch-blocks, 'theirs' is blockly. - -# Formatting helpers. -indent() { sed 's/^/ /'; } -indent_more() { sed 's/^/\t/'; } -empty_lines() { printf '\n\n'; } - - -empty_lines -echo Cleaning up a merge from Blockly to Scratch-Blocks... - -# Get rid of Blockly's internationalization/messages. This is not usually worth -# scrolling up to look at. -empty_lines -echo Cleaning up Blockly message files... -# Turn on more powerful globbing -shopt -s extglob - -# Having trouble with directories. Let's just go there. -cd msg/json -git rm -f !(en.json|synonyms.json) | indent_more -cd ../.. - -# Having trouble with directories. Let's just go there. -cd msg/js -git rm -f !(en.js) | indent_more -cd ../.. - -# Turn powerful globbing off again -shopt -u extglob - -# Whole directories that we want to get rid of. -empty_lines -echo Removing blockly-specific directories... -dirslist="accessible demos tests/generators appengine blocks local_build" -for directory in $dirslist -do - echo 'Cleaning up' $directory | indent - git rm -rf $directory | indent_more -done - -# Scratch-blocks does not use generators -empty_lines -echo Removing generators... -generated_langs="dart javascript lua php python" -for lang in $generated_langs -do - echo 'Cleaning up' $lang | indent - # Directories containing block generators. - git rm -rf generators/${lang} | indent_more -done - -# Built stuff that we should get rid of. -empty_lines -echo Removing built files... -built_files="blockly_compressed.js \ -blockly_uncompressed.js \ -blockly_accessible_compressed.js \ -blockly_accessible_uncompressed.js \ -blocks_compressed.js \ -dart_compressed.js \ -php_compressed.js \ -python_compressed.js \ -javascript_compressed.js \ -lua_compressed.js" - -for filename in $built_files -do - git rm $filename | indent_more -done - -empty_lines -echo Miscellaneous cleanup... -# Use ours. -keep_ours=".github/ISSUE_TEMPLATE.md \ -.github/PULL_REQUEST_TEMPLATE.md \ -.gitignore \ -.circleci/config.yml \ -core/block_animations.js \ -msg/messages.js \ -msg/js/en.js \ -msg/json/en.json" - - -for filename in $keep_ours -do - git checkout --ours $filename && git add $filename | indent_more -done - -# Scratch-blocks has separate vertical and horizontal playgrounds and block -# rendering. -git rm -f tests/playground.html core/block_render_svg.js | indent_more - -empty_lines -echo Done with cleanup. diff --git a/closure-library/AUTHORS b/closure-library/AUTHORS new file mode 100644 index 0000000000..604fc9cbf7 --- /dev/null +++ b/closure-library/AUTHORS @@ -0,0 +1,27 @@ +# This is a list of contributors to the Closure Library. + +# Names should be added to this file like so: +# Name or Organization + +Google Inc. +gigmade ltd. +Mohamed Mansour +Bjorn Tipling +SameGoal LLC +Guido Tapia +Andrew Mattie +Ilia Mirkin +Ivan Kozik +Rich Dougherty +Chad Killingsworth +Dan Pupius +Mike Dunn +Kengo Toda +Remember The Milk Inc. +Anish Visaria +John Huân Vũ +Peter Lu +Filipe Catraia +Dan Rubalsky +Michael Zhou +mash diff --git a/closure-library/CONTRIBUTING b/closure-library/CONTRIBUTING new file mode 100644 index 0000000000..2f0a748e02 --- /dev/null +++ b/closure-library/CONTRIBUTING @@ -0,0 +1,44 @@ +Closure Library welcomes patches/pulls for features and bugfixes. + +For contributors external to Google, follow the instructions given here: + +Notes on Contributions to Closure Library + +Google Individual Contributor License + +In all cases, contributors must sign a contributor license agreement, +either for an individual or corporation, before a patch can be +accepted. Please fill out the agreement for an individual or a +corporation, as appropriate. + +https://developers.google.com/open-source/cla/individual +https://developers.google.com/open-source/cla/corporate + +If you or your organization is not listed there already, you should +add an entry to the AUTHORS file as part of your patch. + +If you plan to add a significant component or large chunk of code, it +is recommended to bring it up on the discussion list for a design +discussion before writing code. + +If appropriate, write a unit test that demonstrates your patch. Tests are the +best way to ensure that future contributors do not break your code +accidentally. + +To change the Closure Library source, you must submit a pull request +in GitHub. See the GitHub documentation here: + +https://help.github.com/categories/63/articles + +Closure Library developers monitor outstanding pull requests. They may +request changes on the pull request before accepting. They will also +verify that the CLA has been signed. + +Oftentimes, the pull request will not be directly merged, but patched to +the internal Google codebase to verify that unit and integration tests +will pass before submitting (and optionally make changes to the patch to +match style, fix text, or to make the code or comments clearer). In this +case, the issue associated with the pull request will be closed when the +patch is pushed to the repository via the MOE (Make Open Easy) system. + +https://github.com/google/MOE/ diff --git a/LICENSE b/closure-library/LICENSE similarity index 99% rename from LICENSE rename to closure-library/LICENSE index 6a1992987f..d9a10c0d8e 100644 --- a/LICENSE +++ b/closure-library/LICENSE @@ -1,6 +1,5 @@ - Apache License - Version 2.0, January 2011 + Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION diff --git a/closure-library/README.md b/closure-library/README.md new file mode 100644 index 0000000000..90992ec0ad --- /dev/null +++ b/closure-library/README.md @@ -0,0 +1,38 @@ +# Closure Library [![Build Status](https://travis-ci.org/google/closure-library.svg?branch=master)](https://travis-ci.org/google/closure-library) + +Closure Library is a powerful, low-level JavaScript library designed +for building complex and scalable web applications. It is used by many +Google web applications, such as Google Search, Gmail, Google Docs, +Google+, Google Maps, and others. + +For more information, visit the +[Google Developers](https://developers.google.com/closure/library) or +[GitHub](https://github.com/google/closure-library) sites. + +Download the latest stable version on our [releases page](https://github.com/google/closure-library/releases). + +Developers, please see the +[Generated API Documentation](https://google.github.io/closure-library/api/). + +See also the +[goog.ui Demos](https://google.github.io/closure-library/source/closure/goog/demos/) + +## Using with Node.js +Install the [official package](https://www.npmjs.com/package/google-closure-library) from npm. + +```bash +npm install google-closure-library +``` + +Require the package and use goog.require normally. + +```js +require("google-closure-library"); + +goog.require("goog.crypt.Sha1"); + +var sha1 = new goog.crypt.Sha1(); +sha1.update("foobar"); +var hash = sha1.digest(); +``` + diff --git a/closure-library/closure/bin/__pycache__/calcdeps.cpython-312.pyc b/closure-library/closure/bin/__pycache__/calcdeps.cpython-312.pyc new file mode 100644 index 0000000000..cdbdc1b67a Binary files /dev/null and b/closure-library/closure/bin/__pycache__/calcdeps.cpython-312.pyc differ diff --git a/closure-library/closure/bin/build/closurebuilder.py b/closure-library/closure/bin/build/closurebuilder.py new file mode 100644 index 0000000000..b542bd0d1b --- /dev/null +++ b/closure-library/closure/bin/build/closurebuilder.py @@ -0,0 +1,293 @@ +#!/usr/bin/env python +# +# Copyright 2009 The Closure Library Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Utility for Closure Library dependency calculation. + +ClosureBuilder scans source files to build dependency info. From the +dependencies, the script can produce a manifest in dependency order, +a concatenated script, or compiled output from the Closure Compiler. + +Paths to files can be expressed as individual arguments to the tool (intended +for use with find and xargs). As a convenience, --root can be used to specify +all JS files below a directory. + +usage: %prog [options] [file1.js file2.js ...] +""" + +__author__ = 'nnaze@google.com (Nathan Naze)' + + +import io +import logging +import optparse +import os +import sys + +import depstree +import jscompiler +import source +import treescan + + +def _GetOptionsParser(): + """Get the options parser.""" + + parser = optparse.OptionParser(__doc__) + parser.add_option('-i', + '--input', + dest='inputs', + action='append', + default=[], + help='One or more input files to calculate dependencies ' + 'for. The namespaces in this file will be combined with ' + 'those given with the -n flag to form the set of ' + 'namespaces to find dependencies for.') + parser.add_option('-n', + '--namespace', + dest='namespaces', + action='append', + default=[], + help='One or more namespaces to calculate dependencies ' + 'for. These namespaces will be combined with those given ' + 'with the -i flag to form the set of namespaces to find ' + 'dependencies for. A Closure namespace is a ' + 'dot-delimited path expression declared with a call to ' + 'goog.provide() (e.g. "goog.array" or "foo.bar").') + parser.add_option('--root', + dest='roots', + action='append', + default=[], + help='The paths that should be traversed to build the ' + 'dependencies.') + parser.add_option('-o', + '--output_mode', + dest='output_mode', + type='choice', + action='store', + choices=['list', 'script', 'compiled'], + default='list', + help='The type of output to generate from this script. ' + 'Options are "list" for a list of filenames, "script" ' + 'for a single script containing the contents of all the ' + 'files, or "compiled" to produce compiled output with ' + 'the Closure Compiler. Default is "list".') + parser.add_option('-c', + '--compiler_jar', + dest='compiler_jar', + action='store', + help='The location of the Closure compiler .jar file.') + parser.add_option('-f', + '--compiler_flags', + dest='compiler_flags', + default=[], + action='append', + help='Additional flags to pass to the Closure compiler. ' + 'To pass multiple flags, --compiler_flags has to be ' + 'specified multiple times.') + parser.add_option('-j', + '--jvm_flags', + dest='jvm_flags', + default=[], + action='append', + help='Additional flags to pass to the JVM compiler. ' + 'To pass multiple flags, --jvm_flags has to be ' + 'specified multiple times.') + parser.add_option('--output_file', + dest='output_file', + action='store', + help=('If specified, write output to this path instead of ' + 'writing to standard output.')) + + return parser + + +def _GetInputByPath(path, sources): + """Get the source identified by a path. + + Args: + path: str, A path to a file that identifies a source. + sources: An iterable collection of source objects. + + Returns: + The source from sources identified by path, if found. Converts to + real paths for comparison. + """ + for js_source in sources: + # Convert both to real paths for comparison. + if os.path.realpath(path) == os.path.realpath(js_source.GetPath()): + return js_source + + +def _GetClosureBaseFile(sources): + """Given a set of sources, returns the one base.js file. + + Note that if zero or two or more base.js files are found, an error message + will be written and the program will be exited. + + Args: + sources: An iterable of _PathSource objects. + + Returns: + The _PathSource representing the base Closure file. + """ + base_files = [ + js_source for js_source in sources if _IsClosureBaseFile(js_source) + ] + + if not base_files: + logging.error('No Closure base.js file found.') + sys.exit(1) + if len(base_files) > 1: + logging.error('More than one Closure base.js files found at these paths:') + for base_file in base_files: + logging.error(base_file.GetPath()) + sys.exit(1) + return base_files[0] + + +def _IsClosureBaseFile(js_source): + """Returns true if the given _PathSource is the Closure base.js source.""" + return (os.path.basename(js_source.GetPath()) == 'base.js' and + js_source.provides == set(['goog'])) + + +class _PathSource(source.Source): + """Source file subclass that remembers its file path.""" + + def __init__(self, path): + """Initialize a source. + + Args: + path: str, Path to a JavaScript file. The source string will be read + from this file. + """ + super(_PathSource, self).__init__(source.GetFileContents(path)) + + self._path = path + + def __str__(self): + return 'PathSource %s' % self._path + + def GetPath(self): + """Returns the path.""" + return self._path + + +def _WrapGoogModuleSource(src): + return (u'goog.loadModule(function(exports) {{' + '"use strict";' + '{0}' + '\n' # terminate any trailing single line comment. + ';return exports' + '}});\n').format(src) + + +def main(): + logging.basicConfig(format=(sys.argv[0] + ': %(message)s'), + level=logging.INFO) + options, args = _GetOptionsParser().parse_args() + + # Make our output pipe. + if options.output_file: + out = io.open(options.output_file, 'wb') + else: + version = sys.version_info[:2] + if version >= (3, 0): + # Write bytes to stdout + out = sys.stdout.buffer + else: + out = sys.stdout + + sources = set() + + logging.info('Scanning paths...') + for path in options.roots: + for js_path in treescan.ScanTreeForJsFiles(path): + sources.add(_PathSource(js_path)) + + # Add scripts specified on the command line. + for js_path in args: + sources.add(_PathSource(js_path)) + + logging.info('%s sources scanned.', len(sources)) + + # Though deps output doesn't need to query the tree, we still build it + # to validate dependencies. + logging.info('Building dependency tree..') + tree = depstree.DepsTree(sources) + + input_namespaces = set() + inputs = options.inputs or [] + for input_path in inputs: + js_input = _GetInputByPath(input_path, sources) + if not js_input: + logging.error('No source matched input %s', input_path) + sys.exit(1) + input_namespaces.update(js_input.provides) + + input_namespaces.update(options.namespaces) + + if not input_namespaces: + logging.error('No namespaces found. At least one namespace must be ' + 'specified with the --namespace or --input flags.') + sys.exit(2) + + # The Closure Library base file must go first. + base = _GetClosureBaseFile(sources) + deps = [base] + tree.GetDependencies(input_namespaces) + + output_mode = options.output_mode + if output_mode == 'list': + out.writelines([js_source.GetPath() + '\n' for js_source in deps]) + elif output_mode == 'script': + for js_source in deps: + src = js_source.GetSource() + if js_source.is_goog_module: + src = _WrapGoogModuleSource(src) + out.write(src.encode('utf-8') + b'\n') + elif output_mode == 'compiled': + logging.warning("""\ +Closure Compiler now natively understands and orders Closure dependencies and +is prefererred over using this script for performing JavaScript compilation. + +Please migrate your codebase. + +See: +https://github.com/google/closure-compiler/wiki/Managing-Dependencies +""") + + # Make sure a .jar is specified. + if not options.compiler_jar: + logging.error('--compiler_jar flag must be specified if --output is ' + '"compiled"') + sys.exit(2) + + # Will throw an error if the compilation fails. + compiled_source = jscompiler.Compile(options.compiler_jar, + [js_source.GetPath() + for js_source in deps], + jvm_flags=options.jvm_flags, + compiler_flags=options.compiler_flags) + + logging.info('JavaScript compilation succeeded.') + out.write(compiled_source.encode('utf-8')) + + else: + logging.error('Invalid value for --output flag.') + sys.exit(2) + + +if __name__ == '__main__': + main() diff --git a/closure-library/closure/bin/build/depstree.py b/closure-library/closure/bin/build/depstree.py new file mode 100644 index 0000000000..f288dd3aa6 --- /dev/null +++ b/closure-library/closure/bin/build/depstree.py @@ -0,0 +1,189 @@ +# Copyright 2009 The Closure Library Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +"""Class to represent a full Closure Library dependency tree. + +Offers a queryable tree of dependencies of a given set of sources. The tree +will also do logical validation to prevent duplicate provides and circular +dependencies. +""" + +__author__ = 'nnaze@google.com (Nathan Naze)' + + +class DepsTree(object): + """Represents the set of dependencies between source files.""" + + def __init__(self, sources): + """Initializes the tree with a set of sources. + + Args: + sources: A set of JavaScript sources. + + Raises: + MultipleProvideError: A namespace is provided by muplitple sources. + NamespaceNotFoundError: A namespace is required but never provided. + """ + + self._sources = sources + self._provides_map = dict() + + # Ensure nothing was provided twice. + for source in sources: + for provide in source.provides: + if provide in self._provides_map: + raise MultipleProvideError( + provide, [self._provides_map[provide], source]) + + self._provides_map[provide] = source + + # Check that all required namespaces are provided. + for source in sources: + for require in source.requires: + if require not in self._provides_map: + raise NamespaceNotFoundError(require, source) + + def GetDependencies(self, required_namespaces): + """Get source dependencies, in order, for the given namespaces. + + Args: + required_namespaces: A string (for one) or list (for one or more) of + namespaces. + + Returns: + A list of source objects that provide those namespaces and all + requirements, in dependency order. + + Raises: + NamespaceNotFoundError: A namespace is requested but doesn't exist. + CircularDependencyError: A cycle is detected in the dependency tree. + """ + if isinstance(required_namespaces, str): + required_namespaces = [required_namespaces] + + deps_sources = [] + + for namespace in required_namespaces: + for source in DepsTree._ResolveDependencies( + namespace, [], self._provides_map, []): + if source not in deps_sources: + deps_sources.append(source) + + return deps_sources + + @staticmethod + def _ResolveDependencies(required_namespace, deps_list, provides_map, + traversal_path): + """Resolve dependencies for Closure source files. + + Follows the dependency tree down and builds a list of sources in dependency + order. This function will recursively call itself to fill all dependencies + below the requested namespaces, and then append its sources at the end of + the list. + + Args: + required_namespace: String of required namespace. + deps_list: List of sources in dependency order. This function will append + the required source once all of its dependencies are satisfied. + provides_map: Map from namespace to source that provides it. + traversal_path: List of namespaces of our path from the root down the + dependency/recursion tree. Used to identify cyclical dependencies. + This is a list used as a stack -- when the function is entered, the + current namespace is pushed and popped right before returning. + Each recursive call will check that the current namespace does not + appear in the list, throwing a CircularDependencyError if it does. + + Returns: + The given deps_list object filled with sources in dependency order. + + Raises: + NamespaceNotFoundError: A namespace is requested but doesn't exist. + CircularDependencyError: A cycle is detected in the dependency tree. + """ + + source = provides_map.get(required_namespace) + if not source: + raise NamespaceNotFoundError(required_namespace) + + if required_namespace in traversal_path: + traversal_path.append(required_namespace) # do this *after* the test + + # This must be a cycle. + raise CircularDependencyError(traversal_path) + + # If we don't have the source yet, we'll have to visit this namespace and + # add the required dependencies to deps_list. + if source not in deps_list: + traversal_path.append(required_namespace) + + for require in source.requires: + + # Append all other dependencies before we append our own. + DepsTree._ResolveDependencies(require, deps_list, provides_map, + traversal_path) + deps_list.append(source) + + traversal_path.pop() + + return deps_list + + +class BaseDepsTreeError(Exception): + """Base DepsTree error.""" + + def __init__(self): + Exception.__init__(self) + + +class CircularDependencyError(BaseDepsTreeError): + """Raised when a dependency cycle is encountered.""" + + def __init__(self, dependency_list): + BaseDepsTreeError.__init__(self) + self._dependency_list = dependency_list + + def __str__(self): + return ('Encountered circular dependency:\n%s\n' % + '\n'.join(self._dependency_list)) + + +class MultipleProvideError(BaseDepsTreeError): + """Raised when a namespace is provided more than once.""" + + def __init__(self, namespace, sources): + BaseDepsTreeError.__init__(self) + self._namespace = namespace + self._sources = sources + + def __str__(self): + source_strs = map(str, self._sources) + + return ('Namespace "%s" provided more than once in sources:\n%s\n' % + (self._namespace, '\n'.join(source_strs))) + + +class NamespaceNotFoundError(BaseDepsTreeError): + """Raised when a namespace is requested but not provided.""" + + def __init__(self, namespace, source=None): + BaseDepsTreeError.__init__(self) + self._namespace = namespace + self._source = source + + def __str__(self): + msg = 'Namespace "%s" never provided.' % self._namespace + if self._source: + msg += ' Required in %s' % self._source + return msg diff --git a/closure-library/closure/bin/build/depswriter.py b/closure-library/closure/bin/build/depswriter.py new file mode 100644 index 0000000000..dc5a101906 --- /dev/null +++ b/closure-library/closure/bin/build/depswriter.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python +# +# Copyright 2009 The Closure Library Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +"""Generates out a Closure deps.js file given a list of JavaScript sources. + +Paths can be specified as arguments or (more commonly) specifying trees +with the flags (call with --help for descriptions). + +Usage: depswriter.py [path/to/js1.js [path/to/js2.js] ...] +""" + +import json +import logging +import optparse +import os +import posixpath +import shlex +import sys + +import source +import treescan + + +__author__ = 'nnaze@google.com (Nathan Naze)' + + +def MakeDepsFile(source_map): + """Make a generated deps file. + + Args: + source_map: A dict map of the source path to source.Source object. + + Returns: + str, A generated deps file source. + """ + + # Write in path alphabetical order + paths = sorted(source_map.keys()) + + lines = [] + + for path in paths: + js_source = source_map[path] + + # We don't need to add entries that don't provide anything. + if js_source.provides: + lines.append(_GetDepsLine(path, js_source)) + + return ''.join(lines) + + +def _GetDepsLine(path, js_source): + """Get a deps.js file string for a source.""" + + provides = _ToJsSrc(sorted(js_source.provides)) + requires = _ToJsSrc(sorted(js_source.requires)) + module = "{'module': 'goog'}" if js_source.is_goog_module else '{}' + + return 'goog.addDependency(\'%s\', %s, %s, %s);\n' % ( + path, provides, requires, module) + + +def _ToJsSrc(arr): + """Convert a python arr to a js source string.""" + + return json.dumps(arr).replace('"', '\'') + + +def _GetOptionsParser(): + """Get the options parser.""" + + parser = optparse.OptionParser(__doc__) + + parser.add_option('--output_file', + dest='output_file', + action='store', + help=('If specified, write output to this path instead of ' + 'writing to standard output.')) + parser.add_option('--root', + dest='roots', + default=[], + action='append', + help='A root directory to scan for JS source files. ' + 'Paths of JS files in generated deps file will be ' + 'relative to this path. This flag may be specified ' + 'multiple times.') + parser.add_option('--root_with_prefix', + dest='roots_with_prefix', + default=[], + action='append', + help='A root directory to scan for JS source files, plus ' + 'a prefix (if either contains a space, surround with ' + 'quotes). Paths in generated deps file will be relative ' + 'to the root, but preceded by the prefix. This flag ' + 'may be specified multiple times.') + parser.add_option('--path_with_depspath', + dest='paths_with_depspath', + default=[], + action='append', + help='A path to a source file and an alternate path to ' + 'the file in the generated deps file (if either contains ' + 'a space, surround with whitespace). This flag may be ' + 'specified multiple times.') + return parser + + +def _NormalizePathSeparators(path): + """Replaces OS-specific path separators with POSIX-style slashes. + + Args: + path: str, A file path. + + Returns: + str, The path with any OS-specific path separators (such as backslash on + Windows) replaced with URL-compatible forward slashes. A no-op on systems + that use POSIX paths. + """ + return path.replace(os.sep, posixpath.sep) + + +def _GetRelativePathToSourceDict(root, prefix=''): + """Scans a top root directory for .js sources. + + Args: + root: str, Root directory. + prefix: str, Prefix for returned paths. + + Returns: + dict, A map of relative paths (with prefix, if given), to source.Source + objects. + """ + # Remember and restore the cwd when we're done. We work from the root so + # that paths are relative from the root. + start_wd = os.getcwd() + os.chdir(root) + + path_to_source = {} + for path in treescan.ScanTreeForJsFiles('.'): + prefixed_path = _NormalizePathSeparators(os.path.join(prefix, path)) + path_to_source[prefixed_path] = source.Source(source.GetFileContents(path)) + + os.chdir(start_wd) + + return path_to_source + + +def _GetPair(s): + """Return a string as a shell-parsed tuple. Two values expected.""" + try: + # shlex uses '\' as an escape character, so they must be escaped. + s = s.replace('\\', '\\\\') + first, second = shlex.split(s) + return (first, second) + except: + raise Exception('Unable to parse input line as a pair: %s' % s) + + +def main(): + """CLI frontend to MakeDepsFile.""" + logging.basicConfig(format=(sys.argv[0] + ': %(message)s'), + level=logging.INFO) + options, args = _GetOptionsParser().parse_args() + + path_to_source = {} + + # Roots without prefixes + for root in options.roots: + path_to_source.update(_GetRelativePathToSourceDict(root)) + + # Roots with prefixes + for root_and_prefix in options.roots_with_prefix: + root, prefix = _GetPair(root_and_prefix) + path_to_source.update(_GetRelativePathToSourceDict(root, prefix=prefix)) + + # Source paths + for path in args: + path_to_source[path] = source.Source(source.GetFileContents(path)) + + # Source paths with alternate deps paths + for path_with_depspath in options.paths_with_depspath: + srcpath, depspath = _GetPair(path_with_depspath) + path_to_source[depspath] = source.Source(source.GetFileContents(srcpath)) + + # Make our output pipe. + if options.output_file: + out = open(options.output_file, 'w') + else: + out = sys.stdout + + out.write(('// This file was autogenerated by %s.\n' % + os.path.basename(__file__))) + out.write('// Please do not edit.\n') + + out.write(MakeDepsFile(path_to_source)) + + +if __name__ == '__main__': + main() diff --git a/closure-library/closure/bin/build/jscompiler.py b/closure-library/closure/bin/build/jscompiler.py new file mode 100644 index 0000000000..249e1fc620 --- /dev/null +++ b/closure-library/closure/bin/build/jscompiler.py @@ -0,0 +1,161 @@ +# Copyright 2010 The Closure Library Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Utility to use the Closure Compiler CLI from Python.""" + +import logging +import os +import re +import subprocess +import tempfile + +# Pulls just the major and minor version numbers from the first line of +# 'java -version'. Versions are in the format of [0-9]+(\.[0-9]+)? See: +# http://openjdk.java.net/jeps/223 +_VERSION_REGEX = re.compile(r'"([0-9]+)(?:\.([0-9]+))?') + + +class JsCompilerError(Exception): + """Raised if there's an error in calling the compiler.""" + pass + + +def _GetJavaVersionString(): + """Get the version string from the Java VM.""" + return subprocess.check_output(['java', '-version'], stderr=subprocess.STDOUT) + + +def _ParseJavaVersion(version_string): + """Returns a 2-tuple for the current version of Java installed. + + Args: + version_string: String of the Java version (e.g. '1.7.2-ea'). + + Returns: + The major and minor versions, as a 2-tuple (e.g. (1, 7)). + """ + match = _VERSION_REGEX.search(version_string) + if match: + version = tuple(int(x or 0) for x in match.groups()) + assert len(version) == 2 + return version + + +def _JavaSupports32BitMode(): + """Determines whether the JVM supports 32-bit mode on the platform.""" + # Suppresses process output to stderr and stdout from showing up in the + # console as we're only trying to determine 32-bit JVM support. + supported = False + try: + devnull = open(os.devnull, 'wb') + return subprocess.call( + ['java', '-d32', '-version'], stdout=devnull, stderr=devnull) == 0 + except IOError: + pass + else: + devnull.close() + return supported + + +def _GetJsCompilerArgs(compiler_jar_path, java_version, jvm_flags): + """Assembles arguments for call to JsCompiler.""" + + if java_version < (1, 7): + raise JsCompilerError('Closure Compiler requires Java 1.7 or higher. ' + 'Please visit http://www.java.com/getjava') + + args = ['java'] + + # Add JVM flags we believe will produce the best performance. See + # https://groups.google.com/forum/#!topic/closure-library-discuss/7w_O9-vzlj4 + + # Attempt 32-bit mode if available (Java 7 on Mac OS X does not support 32-bit + # mode, for example). + if _JavaSupports32BitMode(): + args += ['-d32'] + + # Prefer the "client" VM. + args += ['-client'] + + # Add JVM flags, if any + if jvm_flags: + args += jvm_flags + + # Add the application JAR. + args += ['-jar', compiler_jar_path] + + return args + + +def _GetFlagFile(source_paths, compiler_flags): + """Writes given source paths and compiler flags to a --flagfile. + + The given source_paths will be written as '--js' flags and the compiler_flags + are written as-is. + + Args: + source_paths: List of string js source paths. + compiler_flags: List of string compiler flags. + + Returns: + The file to which the flags were written. + """ + args = [] + for path in source_paths: + args += ['--js', path] + + # Add compiler flags, if any. + if compiler_flags: + args += compiler_flags + + flags_file = tempfile.NamedTemporaryFile(delete=False) + flags_file.write(' '.join(args)) + flags_file.close() + + return flags_file + + +def Compile(compiler_jar_path, + source_paths, + jvm_flags=None, + compiler_flags=None): + """Prepares command-line call to Closure Compiler. + + Args: + compiler_jar_path: Path to the Closure compiler .jar file. + source_paths: Source paths to build, in order. + jvm_flags: A list of additional flags to pass on to JVM. + compiler_flags: A list of additional flags to pass on to Closure Compiler. + + Returns: + The compiled source, as a string, or None if compilation failed. + """ + + java_version = _ParseJavaVersion(str(_GetJavaVersionString())) + + args = _GetJsCompilerArgs(compiler_jar_path, java_version, jvm_flags) + + # Write source path arguments to flag file for avoiding "The filename or + # extension is too long" error in big projects. See + # https://github.com/google/closure-library/pull/678 + flags_file = _GetFlagFile(source_paths, compiler_flags) + args += ['--flagfile', flags_file.name] + + logging.info('Compiling with the following command: %s', ' '.join(args)) + + try: + return subprocess.check_output(args) + except subprocess.CalledProcessError: + raise JsCompilerError('JavaScript compilation failed.') + finally: + os.remove(flags_file.name) diff --git a/closure-library/closure/bin/build/source.py b/closure-library/closure/bin/build/source.py new file mode 100644 index 0000000000..447b2f0aa9 --- /dev/null +++ b/closure-library/closure/bin/build/source.py @@ -0,0 +1,132 @@ +# Copyright 2009 The Closure Library Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +"""Scans a source JS file for its provided and required namespaces. + +Simple class to scan a JavaScript file and express its dependencies. +""" + +__author__ = 'nnaze@google.com' + + +import codecs +import re + +_BASE_REGEX_STRING = r'^\s*goog\.%s\(\s*[\'"](.+)[\'"]\s*\)' +_MODULE_REGEX = re.compile(_BASE_REGEX_STRING % 'module') +_PROVIDE_REGEX = re.compile(_BASE_REGEX_STRING % 'provide') + +_REQUIRE_REGEX_STRING = (r'^\s*(?:(?:var|let|const)\s+[a-zA-Z0-9$_,:{}\s]*' + r'\s*=\s*)?goog\.require\(\s*[\'"](.+)[\'"]\s*\)') +_REQUIRES_REGEX = re.compile(_REQUIRE_REGEX_STRING) + +class Source(object): + """Scans a JavaScript source for its provided and required namespaces.""" + + # Matches a "/* ... */" comment. + # Note: We can't definitively distinguish a "/*" in a string literal without a + # state machine tokenizer. We'll assume that a line starting with whitespace + # and "/*" is a comment. + _COMMENT_REGEX = re.compile( + r""" + ^\s* # Start of a new line and whitespace + /\* # Opening "/*" + .*? # Non greedy match of any characters (including newlines) + \*/ # Closing "*/""", + re.MULTILINE | re.DOTALL | re.VERBOSE) + + def __init__(self, source): + """Initialize a source. + + Args: + source: str, The JavaScript source. + """ + + self.provides = set() + self.requires = set() + self.is_goog_module = False + + self._source = source + self._ScanSource() + + def GetSource(self): + """Get the source as a string.""" + return self._source + + @classmethod + def _StripComments(cls, source): + return cls._COMMENT_REGEX.sub('', source) + + @classmethod + def _HasProvideGoogFlag(cls, source): + """Determines whether the @provideGoog flag is in a comment.""" + for comment_content in cls._COMMENT_REGEX.findall(source): + if '@provideGoog' in comment_content: + return True + + return False + + def _ScanSource(self): + """Fill in provides and requires by scanning the source.""" + + stripped_source = self._StripComments(self.GetSource()) + + source_lines = stripped_source.splitlines() + for line in source_lines: + match = _PROVIDE_REGEX.match(line) + if match: + self.provides.add(match.group(1)) + match = _MODULE_REGEX.match(line) + if match: + self.provides.add(match.group(1)) + self.is_goog_module = True + match = _REQUIRES_REGEX.match(line) + if match: + self.requires.add(match.group(1)) + + # Closure's base file implicitly provides 'goog'. + # This is indicated with the @provideGoog flag. + if self._HasProvideGoogFlag(self.GetSource()): + + if len(self.provides) or len(self.requires): + raise Exception( + 'Base file should not provide or require namespaces.') + + self.provides.add('goog') + + +def GetFileContents(path): + """Get a file's contents as a string. + + Args: + path: str, Path to file. + + Returns: + str, Contents of file. + + Raises: + IOError: An error occurred opening or reading the file. + + """ + fileobj = None + try: + fileobj = codecs.open(path, encoding='utf-8-sig') + return fileobj.read() + except IOError as error: + raise IOError('An error occurred opening or reading the file: %s. %s' + % (path, error)) + finally: + if fileobj is not None: + fileobj.close() diff --git a/closure-library/closure/bin/build/treescan.py b/closure-library/closure/bin/build/treescan.py new file mode 100644 index 0000000000..6694593aab --- /dev/null +++ b/closure-library/closure/bin/build/treescan.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# +# Copyright 2010 The Closure Library Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +"""Shared utility functions for scanning directory trees.""" + +import os +import re + + +__author__ = 'nnaze@google.com (Nathan Naze)' + + +# Matches a .js file path. +_JS_FILE_REGEX = re.compile(r'^.+\.js$') + + +def ScanTreeForJsFiles(root): + """Scans a directory tree for JavaScript files. + + Args: + root: str, Path to a root directory. + + Returns: + An iterable of paths to JS files, relative to cwd. + """ + return ScanTree(root, path_filter=_JS_FILE_REGEX) + + +def ScanTree(root, path_filter=None, ignore_hidden=True): + """Scans a directory tree for files. + + Args: + root: str, Path to a root directory. + path_filter: A regular expression filter. If set, only paths matching + the path_filter are returned. + ignore_hidden: If True, do not follow or return hidden directories or files + (those starting with a '.' character). + + Yields: + A string path to files, relative to cwd. + """ + + def OnError(os_error): + raise os_error + + for dirpath, dirnames, filenames in os.walk(root, onerror=OnError): + # os.walk allows us to modify dirnames to prevent decent into particular + # directories. Avoid hidden directories. + for dirname in dirnames: + if ignore_hidden and dirname.startswith('.'): + dirnames.remove(dirname) + + for filename in filenames: + + # nothing that starts with '.' + if ignore_hidden and filename.startswith('.'): + continue + + fullpath = os.path.join(dirpath, filename) + + if path_filter and not path_filter.match(fullpath): + continue + + yield os.path.normpath(fullpath) diff --git a/closure-library/closure/bin/calcdeps.py b/closure-library/closure/bin/calcdeps.py new file mode 100644 index 0000000000..d94296419a --- /dev/null +++ b/closure-library/closure/bin/calcdeps.py @@ -0,0 +1,590 @@ +#!/usr/bin/env python +# +# Copyright 2006 The Closure Library Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +"""Calculates JavaScript dependencies without requiring Google's build system. + +This tool is deprecated and is provided for legacy users. +See build/closurebuilder.py and build/depswriter.py for the current tools. + +It iterates over a number of search paths and builds a dependency tree. With +the inputs provided, it walks the dependency tree and outputs all the files +required for compilation. +""" + + + + + +try: + import distutils.version +except ImportError: + # distutils is not available in all environments + distutils = None + +import logging +import optparse +import os +import re +import subprocess +import sys + + +_BASE_REGEX_STRING = '^\s*goog\.%s\(\s*[\'"](.+)[\'"]\s*\)' +req_regex = re.compile(_BASE_REGEX_STRING % 'require') +prov_regex = re.compile(_BASE_REGEX_STRING % 'provide') +ns_regex = re.compile('^ns:((\w+\.)*(\w+))$') +version_regex = re.compile('[\.0-9]+') + + +def IsValidFile(ref): + """Returns true if the provided reference is a file and exists.""" + return os.path.isfile(ref) + + +def IsJsFile(ref): + """Returns true if the provided reference is a JavaScript file.""" + return ref.endswith('.js') + + +def IsNamespace(ref): + """Returns true if the provided reference is a namespace.""" + return re.match(ns_regex, ref) is not None + + +def IsDirectory(ref): + """Returns true if the provided reference is a directory.""" + return os.path.isdir(ref) + + +def ExpandDirectories(refs): + """Expands any directory references into inputs. + + Description: + Looks for any directories in the provided references. Found directories + are recursively searched for .js files, which are then added to the result + list. + + Args: + refs: a list of references such as files, directories, and namespaces + + Returns: + A list of references with directories removed and replaced by any + .js files that are found in them. Also, the paths will be normalized. + """ + result = [] + for ref in refs: + if IsDirectory(ref): + # Disable 'Unused variable' for subdirs + # pylint: disable=unused-variable + for (directory, subdirs, filenames) in os.walk(ref): + for filename in filenames: + if IsJsFile(filename): + result.append(os.path.join(directory, filename)) + else: + result.append(ref) + return map(os.path.normpath, result) + + +class DependencyInfo(object): + """Represents a dependency that is used to build and walk a tree.""" + + def __init__(self, filename): + self.filename = filename + self.provides = [] + self.requires = [] + + def __str__(self): + return '%s Provides: %s Requires: %s' % (self.filename, + repr(self.provides), + repr(self.requires)) + + +def BuildDependenciesFromFiles(files): + """Build a list of dependencies from a list of files. + + Description: + Takes a list of files, extracts their provides and requires, and builds + out a list of dependency objects. + + Args: + files: a list of files to be parsed for goog.provides and goog.requires. + + Returns: + A list of dependency objects, one for each file in the files argument. + """ + result = [] + filenames = set() + for filename in files: + if filename in filenames: + continue + + # Python 3 requires the file encoding to be specified + if (sys.version_info[0] < 3): + file_handle = open(filename, 'r') + else: + file_handle = open(filename, 'r', encoding='utf8') + + try: + dep = CreateDependencyInfo(filename, file_handle) + result.append(dep) + finally: + file_handle.close() + + filenames.add(filename) + + return result + + +def CreateDependencyInfo(filename, source): + """Create dependency info. + + Args: + filename: Filename for source. + source: File-like object containing source. + + Returns: + A DependencyInfo object with provides and requires filled. + """ + dep = DependencyInfo(filename) + for line in source: + if re.match(req_regex, line): + dep.requires.append(re.search(req_regex, line).group(1)) + if re.match(prov_regex, line): + dep.provides.append(re.search(prov_regex, line).group(1)) + return dep + + +def BuildDependencyHashFromDependencies(deps): + """Builds a hash for searching dependencies by the namespaces they provide. + + Description: + Dependency objects can provide multiple namespaces. This method enumerates + the provides of each dependency and adds them to a hash that can be used + to easily resolve a given dependency by a namespace it provides. + + Args: + deps: a list of dependency objects used to build the hash. + + Raises: + Exception: If a multiple files try to provide the same namepace. + + Returns: + A hash table { namespace: dependency } that can be used to resolve a + dependency by a namespace it provides. + """ + dep_hash = {} + for dep in deps: + for provide in dep.provides: + if provide in dep_hash: + raise Exception('Duplicate provide (%s) in (%s, %s)' % ( + provide, + dep_hash[provide].filename, + dep.filename)) + dep_hash[provide] = dep + return dep_hash + + +def CalculateDependencies(paths, inputs): + """Calculates the dependencies for given inputs. + + Description: + This method takes a list of paths (files, directories) and builds a + searchable data structure based on the namespaces that each .js file + provides. It then parses through each input, resolving dependencies + against this data structure. The final output is a list of files, + including the inputs, that represent all of the code that is needed to + compile the given inputs. + + Args: + paths: the references (files, directories) that are used to build the + dependency hash. + inputs: the inputs (files, directories, namespaces) that have dependencies + that need to be calculated. + + Raises: + Exception: if a provided input is invalid. + + Returns: + A list of all files, including inputs, that are needed to compile the given + inputs. + """ + deps = BuildDependenciesFromFiles(paths + inputs) + search_hash = BuildDependencyHashFromDependencies(deps) + result_list = [] + seen_list = [] + for input_file in inputs: + if IsNamespace(input_file): + namespace = re.search(ns_regex, input_file).group(1) + if namespace not in search_hash: + raise Exception('Invalid namespace (%s)' % namespace) + input_file = search_hash[namespace].filename + if not IsValidFile(input_file) or not IsJsFile(input_file): + raise Exception('Invalid file (%s)' % input_file) + seen_list.append(input_file) + file_handle = open(input_file, 'r') + try: + for line in file_handle: + if re.match(req_regex, line): + require = re.search(req_regex, line).group(1) + ResolveDependencies(require, search_hash, result_list, seen_list) + finally: + file_handle.close() + result_list.append(input_file) + + # All files depend on base.js, so put it first. + base_js_path = FindClosureBasePath(paths) + if base_js_path: + result_list.insert(0, base_js_path) + else: + logging.warning('Closure Library base.js not found.') + + return result_list + + +def FindClosureBasePath(paths): + """Given a list of file paths, return Closure base.js path, if any. + + Args: + paths: A list of paths. + + Returns: + The path to Closure's base.js file including filename, if found. + """ + + for path in paths: + pathname, filename = os.path.split(path) + + if filename == 'base.js': + f = open(path) + + is_base = False + + # Sanity check that this is the Closure base file. Check that this + # is where goog is defined. This is determined by the @provideGoog + # flag. + for line in f: + if '@provideGoog' in line: + is_base = True + break + + f.close() + + if is_base: + return path + +def ResolveDependencies(require, search_hash, result_list, seen_list): + """Takes a given requirement and resolves all of the dependencies for it. + + Description: + A given requirement may require other dependencies. This method + recursively resolves all dependencies for the given requirement. + + Raises: + Exception: when require does not exist in the search_hash. + + Args: + require: the namespace to resolve dependencies for. + search_hash: the data structure used for resolving dependencies. + result_list: a list of filenames that have been calculated as dependencies. + This variable is the output for this function. + seen_list: a list of filenames that have been 'seen'. This is required + for the dependency->dependent ordering. + """ + if require not in search_hash: + raise Exception('Missing provider for (%s)' % require) + + dep = search_hash[require] + if not dep.filename in seen_list: + seen_list.append(dep.filename) + for sub_require in dep.requires: + ResolveDependencies(sub_require, search_hash, result_list, seen_list) + result_list.append(dep.filename) + + +def GetDepsLine(dep, base_path): + """Returns a JS string for a dependency statement in the deps.js file. + + Args: + dep: The dependency that we're printing. + base_path: The path to Closure's base.js including filename. + """ + return 'goog.addDependency("%s", %s, %s);' % ( + GetRelpath(dep.filename, base_path), dep.provides, dep.requires) + + +def GetRelpath(path, start): + """Return a relative path to |path| from |start|.""" + # NOTE: Python 2.6 provides os.path.relpath, which has almost the same + # functionality as this function. Since we want to support 2.4, we have + # to implement it manually. :( + path_list = os.path.abspath(os.path.normpath(path)).split(os.sep) + start_list = os.path.abspath( + os.path.normpath(os.path.dirname(start))).split(os.sep) + + common_prefix_count = 0 + for i in range(0, min(len(path_list), len(start_list))): + if path_list[i] != start_list[i]: + break + common_prefix_count += 1 + + # Always use forward slashes, because this will get expanded to a url, + # not a file path. + return '/'.join(['..'] * (len(start_list) - common_prefix_count) + + path_list[common_prefix_count:]) + + +def PrintLine(msg, out): + out.write(msg) + out.write('\n') + + +def PrintDeps(source_paths, deps, out): + """Print out a deps.js file from a list of source paths. + + Args: + source_paths: Paths that we should generate dependency info for. + deps: Paths that provide dependency info. Their dependency info should + not appear in the deps file. + out: The output file. + + Returns: + True on success, false if it was unable to find the base path + to generate deps relative to. + """ + base_path = FindClosureBasePath(source_paths + deps) + if not base_path: + return False + + PrintLine('// This file was autogenerated by calcdeps.py', out) + excludesSet = set(deps) + + for dep in BuildDependenciesFromFiles(source_paths + deps): + if not dep.filename in excludesSet: + PrintLine(GetDepsLine(dep, base_path), out) + + return True + + +def PrintScript(source_paths, out): + for index, dep in enumerate(source_paths): + PrintLine('// Input %d' % index, out) + f = open(dep, 'r') + PrintLine(f.read(), out) + f.close() + + +def GetJavaVersion(): + """Returns the string for the current version of Java installed.""" + proc = subprocess.Popen(['java', '-version'], stderr=subprocess.PIPE) + proc.wait() + version_line = proc.stderr.read().splitlines()[0] + return version_regex.search(version_line.decode('utf-8')).group() + + +def FilterByExcludes(options, files): + """Filters the given files by the exlusions specified at the command line. + + Args: + options: The flags to calcdeps. + files: The files to filter. + Returns: + A list of files. + """ + excludes = [] + if options.excludes: + excludes = ExpandDirectories(options.excludes) + + excludesSet = set(excludes) + return [i for i in files if not i in excludesSet] + + +def GetPathsFromOptions(options): + """Generates the path files from flag options. + + Args: + options: The flags to calcdeps. + Returns: + A list of files in the specified paths. (strings). + """ + + search_paths = options.paths + if not search_paths: + search_paths = ['.'] # Add default folder if no path is specified. + + search_paths = ExpandDirectories(search_paths) + return FilterByExcludes(options, search_paths) + + +def GetInputsFromOptions(options): + """Generates the inputs from flag options. + + Args: + options: The flags to calcdeps. + Returns: + A list of inputs (strings). + """ + inputs = options.inputs + if not inputs: # Parse stdin + logging.info('No inputs specified. Reading from stdin...') + inputs = filter(None, [line.strip('\n') for line in sys.stdin.readlines()]) + + logging.info('Scanning files...') + inputs = ExpandDirectories(inputs) + + return FilterByExcludes(options, inputs) + + +def Compile(compiler_jar_path, source_paths, out, flags=None): + """Prepares command-line call to Closure compiler. + + Args: + compiler_jar_path: Path to the Closure compiler .jar file. + source_paths: Source paths to build, in order. + flags: A list of additional flags to pass on to Closure compiler. + """ + args = ['java', '-jar', compiler_jar_path] + for path in source_paths: + args += ['--js', path] + + if flags: + args += flags + + logging.info('Compiling with the following command: %s', ' '.join(args)) + proc = subprocess.Popen(args, stdout=subprocess.PIPE) + (stdoutdata, stderrdata) = proc.communicate() + if proc.returncode != 0: + logging.error('JavaScript compilation failed.') + sys.exit(1) + else: + out.write(stdoutdata.decode('utf-8')) + + +def main(): + """The entrypoint for this script.""" + + logging.basicConfig(format='calcdeps.py: %(message)s', level=logging.INFO) + + usage = 'usage: %prog [options] arg' + parser = optparse.OptionParser(usage) + parser.add_option('-i', + '--input', + dest='inputs', + action='append', + help='The inputs to calculate dependencies for. Valid ' + 'values can be files, directories, or namespaces ' + '(ns:goog.net.XhrIo). Only relevant to "list" and ' + '"script" output.') + parser.add_option('-p', + '--path', + dest='paths', + action='append', + help='The paths that should be traversed to build the ' + 'dependencies.') + parser.add_option('-d', + '--dep', + dest='deps', + action='append', + help='Directories or files that should be traversed to ' + 'find required dependencies for the deps file. ' + 'Does not generate dependency information for names ' + 'provided by these files. Only useful in "deps" mode.') + parser.add_option('-e', + '--exclude', + dest='excludes', + action='append', + help='Files or directories to exclude from the --path ' + 'and --input flags') + parser.add_option('-o', + '--output_mode', + dest='output_mode', + action='store', + default='list', + help='The type of output to generate from this script. ' + 'Options are "list" for a list of filenames, "script" ' + 'for a single script containing the contents of all the ' + 'file, "deps" to generate a deps.js file for all ' + 'paths, or "compiled" to produce compiled output with ' + 'the Closure compiler.') + parser.add_option('-c', + '--compiler_jar', + dest='compiler_jar', + action='store', + help='The location of the Closure compiler .jar file.') + parser.add_option('-f', + '--compiler_flag', + '--compiler_flags', # for backwards compatibility + dest='compiler_flags', + action='append', + help='Additional flag to pass to the Closure compiler. ' + 'May be specified multiple times to pass multiple flags.') + parser.add_option('--output_file', + dest='output_file', + action='store', + help=('If specified, write output to this path instead of ' + 'writing to standard output.')) + + (options, args) = parser.parse_args() + + search_paths = GetPathsFromOptions(options) + + if options.output_file: + out = open(options.output_file, 'w') + else: + out = sys.stdout + + if options.output_mode == 'deps': + result = PrintDeps(search_paths, ExpandDirectories(options.deps or []), out) + if not result: + logging.error('Could not find Closure Library in the specified paths') + sys.exit(1) + + return + + inputs = GetInputsFromOptions(options) + + logging.info('Finding Closure dependencies...') + deps = CalculateDependencies(search_paths, inputs) + output_mode = options.output_mode + + if output_mode == 'script': + PrintScript(deps, out) + elif output_mode == 'list': + # Just print out a dep per line + for dep in deps: + PrintLine(dep, out) + elif output_mode == 'compiled': + # Make sure a .jar is specified. + if not options.compiler_jar: + logging.error('--compiler_jar flag must be specified if --output is ' + '"compiled"') + sys.exit(1) + + # User friendly version check. + if distutils and not (distutils.version.LooseVersion(GetJavaVersion()) > + distutils.version.LooseVersion('1.6')): + logging.error('Closure Compiler requires Java 1.6 or higher.') + logging.error('Please visit http://www.java.com/getjava') + sys.exit(1) + + Compile(options.compiler_jar, deps, out, options.compiler_flags) + + else: + logging.error('Invalid value for --output flag.') + sys.exit(1) + +if __name__ == '__main__': + main() diff --git a/closure-library/closure/bin/labs/code/closure.el b/closure-library/closure/bin/labs/code/closure.el new file mode 100644 index 0000000000..394c483749 --- /dev/null +++ b/closure-library/closure/bin/labs/code/closure.el @@ -0,0 +1,39 @@ +;; Copyright 2013 The Closure Library Authors. All Rights Reserved. +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required `by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS-IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. + +;; Closure JS code editing functions for emacs. + +;; Author: nnaze@google.com (Nathan Naze) + +;; Remember the path of this file, as we will base our paths on it. +(setq closure-el-path load-file-name) + +(defun closure-el-directory () + "Get the directory the closure.el file lives in." + (file-name-directory closure-el-path)) + +(defun closure-generate-jsdoc-path() + "The path of the generate_jsdoc.py script." + (concat (closure-el-directory) "generate_jsdoc.py")) + +(defun closure-insert-jsdoc () + "Insert JSDoc for the next function after the cursor." + (interactive) + (save-excursion ; Remembers cursor location + (call-process-region + (point) (point-max) + (closure-generate-jsdoc-path) + t t))) + +(provide 'closure) diff --git a/closure-library/closure/bin/labs/code/generate_jsdoc.py b/closure-library/closure/bin/labs/code/generate_jsdoc.py new file mode 100644 index 0000000000..fc5ca45a48 --- /dev/null +++ b/closure-library/closure/bin/labs/code/generate_jsdoc.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python +# +# Copyright 2013 The Closure Library Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tool to insert JsDoc before a function. + +This script attempts to find the first function passed in to stdin, generate +JSDoc for it (with argument names and possibly return value), and inject +it in the string. This is intended to be used as a subprocess by editors +such as emacs and vi. +""" + +import re +import sys + + +# Matches a typical Closure-style function definition. +_FUNCTION_REGEX = re.compile(r""" +# Start of line +^ + +# Indentation +(?P[ ]*) + +# Identifier (handling split across line) +(?P\w+(\s*\.\s*\w+)*) + +# "= function" +\s* = \s* function \s* + +# opening paren +\( + +# Function arguments +(?P(?:\s|\w+|,)*) + +# closing paren +\) + +# opening bracket +\s* { + +""", re.MULTILINE | re.VERBOSE) + + +def _MatchFirstFunction(script): + """Match the first function seen in the script.""" + return _FUNCTION_REGEX.search(script) + + +def _ParseArgString(arg_string): + """Parse an argument string (inside parens) into parameter names.""" + for arg in arg_string.split(','): + arg = arg.strip() + if arg: + yield arg + + +def _ExtractFunctionBody(script, indentation=0): + """Attempt to return the function body.""" + + # Real extraction would require a token parser and state machines. + # We look for first bracket at the same level of indentation. + regex_str = r'{(.*?)^[ ]{%d}}' % indentation + + function_regex = re.compile(regex_str, re.MULTILINE | re.DOTALL) + match = function_regex.search(script) + if match: + return match.group(1) + + +def _ContainsReturnValue(function_body): + """Attempt to determine if the function body returns a value.""" + return_regex = re.compile(r'\breturn\b[^;]') + + # If this matches, we assume they're returning something. + return bool(return_regex.search(function_body)) + + +def _InsertString(original_string, inserted_string, index): + """Insert a string into another string at a given index.""" + return original_string[0:index] + inserted_string + original_string[index:] + + +def _GenerateJsDoc(args, return_val=False): + """Generate JSDoc for a function. + + Args: + args: A list of names of the argument. + return_val: Whether the function has a return value. + + Returns: + The JSDoc as a string. + """ + + lines = [] + lines.append('/**') + + lines += [' * @param {} %s' % arg for arg in args] + + if return_val: + lines.append(' * @return') + + lines.append(' */') + + return '\n'.join(lines) + '\n' + + +def _IndentString(source_string, indentation): + """Indent string some number of characters.""" + lines = [(indentation * ' ') + line + for line in source_string.splitlines(True)] + return ''.join(lines) + + +def InsertJsDoc(script): + """Attempt to insert JSDoc for the first seen function in the script. + + Args: + script: The script, as a string. + + Returns: + Returns the new string if function was found and JSDoc inserted. Otherwise + returns None. + """ + + match = _MatchFirstFunction(script) + if not match: + return + + # Add argument flags. + args_string = match.group('arguments') + args = _ParseArgString(args_string) + + start_index = match.start(0) + function_to_end = script[start_index:] + + lvalue_indentation = len(match.group('indentation')) + + return_val = False + function_body = _ExtractFunctionBody(function_to_end, lvalue_indentation) + if function_body: + return_val = _ContainsReturnValue(function_body) + + jsdoc = _GenerateJsDoc(args, return_val) + if lvalue_indentation: + jsdoc = _IndentString(jsdoc, lvalue_indentation) + + return _InsertString(script, jsdoc, start_index) + + +if __name__ == '__main__': + stdin_script = sys.stdin.read() + result = InsertJsDoc(stdin_script) + + if result: + sys.stdout.write(result) + else: + sys.stdout.write(stdin_script) diff --git a/closure-library/closure/bin/labs/code/run_el_tests.sh b/closure-library/closure/bin/labs/code/run_el_tests.sh new file mode 100644 index 0000000000..ded2a74a11 --- /dev/null +++ b/closure-library/closure/bin/labs/code/run_el_tests.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# +# Copyright 2013 The Closure Library Authors +# +# 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. +# +# Wraps the unit tests not using the Google framework so they may be run +# on the continuous build. +# +# Author: nnaze@google.com (Nathan Naze) +# +# Wraps the unit tests not using the Google framework so they may be run +# run on the continuous build. + +set -e + +source googletest.sh || exit 1 + +CLOSURE_SRCDIR=$TEST_SRCDIR/google3/javascript/closure/labs/bin/code/ + +emacs --script $CLOSURE_SRCDIR/closure_test.el diff --git a/closure-library/closure/bin/labs/code/run_py_tests.sh b/closure-library/closure/bin/labs/code/run_py_tests.sh new file mode 100644 index 0000000000..d075ebba1f --- /dev/null +++ b/closure-library/closure/bin/labs/code/run_py_tests.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# +# Copyright 2013 The Closure Library Authors +# +# 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. +# +# Wraps the unit tests not using the Google framework so they may be run +# on the continuous build. + +set -e + +source googletest.sh || exit 1 + +CLOSURE_SRCDIR=$TEST_SRCDIR/google3/javascript/closure/labs/bin/code/ + +PYTHONPATH=$CLOSURE_SRCDIR + +$CLOSURE_SRCDIR/generate_jsdoc_test.py diff --git a/closure-library/closure/bin/logos/logo.svg b/closure-library/closure/bin/logos/logo.svg new file mode 100644 index 0000000000..2cb6b88a31 --- /dev/null +++ b/closure-library/closure/bin/logos/logo.svg @@ -0,0 +1,25 @@ + + + + + Closure Logo + + + + + diff --git a/closure-library/closure/bin/logos/logoandlabel.svg b/closure-library/closure/bin/logos/logoandlabel.svg new file mode 100644 index 0000000000..69c2d6849b --- /dev/null +++ b/closure-library/closure/bin/logos/logoandlabel.svg @@ -0,0 +1,25 @@ + + + + + + Closure + diff --git a/closure-library/closure/bin/scopify.py b/closure-library/closure/bin/scopify.py new file mode 100644 index 0000000000..d8057efbc9 --- /dev/null +++ b/closure-library/closure/bin/scopify.py @@ -0,0 +1,221 @@ +#!/usr/bin/python +# +# Copyright 2010 The Closure Library Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +"""Automatically converts codebases over to goog.scope. + +Usage: +cd path/to/my/dir; +../../../../javascript/closure/bin/scopify.py + +Scans every file in this directory, recursively. Looks for existing +goog.scope calls, and goog.require'd symbols. If it makes sense to +generate a goog.scope call for the file, then we will do so, and +try to auto-generate some aliases based on the goog.require'd symbols. + +Known Issues: + + When a file is goog.scope'd, the file contents will be indented +2. + This may put some lines over 80 chars. These will need to be fixed manually. + + We will only try to create aliases for capitalized names. We do not check + to see if those names will conflict with any existing locals. + + This creates merge conflicts for every line of every outstanding change. + If you intend to run this on your codebase, make sure your team members + know. Better yet, send them this script so that they can scopify their + outstanding changes and "accept theirs". + + When an alias is "captured", it can no longer be stubbed out for testing. + Run your tests. + +""" + +__author__ = 'nicksantos@google.com (Nick Santos)' + +import os.path +import re +import sys + +REQUIRES_RE = re.compile(r"goog.require\('([^']*)'\)") + +# Edit this manually if you want something to "always" be aliased. +# TODO(nicksantos): Add a flag for this. +DEFAULT_ALIASES = {} + +def Transform(lines): + """Converts the contents of a file into javascript that uses goog.scope. + + Arguments: + lines: A list of strings, corresponding to each line of the file. + Returns: + A new list of strings, or None if the file was not modified. + """ + requires = [] + + # Do an initial scan to be sure that this file can be processed. + for line in lines: + # Skip this file if it has already been scopified. + if line.find('goog.scope') != -1: + return None + + # If there are any global vars or functions, then we also have + # to skip the whole file. We might be able to deal with this + # more elegantly. + if line.find('var ') == 0 or line.find('function ') == 0: + return None + + for match in REQUIRES_RE.finditer(line): + requires.append(match.group(1)) + + if len(requires) == 0: + return None + + # Backwards-sort the requires, so that when one is a substring of another, + # we match the longer one first. + for val in DEFAULT_ALIASES.values(): + if requires.count(val) == 0: + requires.append(val) + + requires.sort() + requires.reverse() + + # Generate a map of requires to their aliases + aliases_to_globals = DEFAULT_ALIASES.copy() + for req in requires: + index = req.rfind('.') + if index == -1: + alias = req + else: + alias = req[(index + 1):] + + # Don't scopify lowercase namespaces, because they may conflict with + # local variables. + if alias[0].isupper(): + aliases_to_globals[alias] = req + + aliases_to_matchers = {} + globals_to_aliases = {} + for alias, symbol in aliases_to_globals.items(): + globals_to_aliases[symbol] = alias + aliases_to_matchers[alias] = re.compile('\\b%s\\b' % symbol) + + # Insert a goog.scope that aliases all required symbols. + result = [] + + START = 0 + SEEN_REQUIRES = 1 + IN_SCOPE = 2 + + mode = START + aliases_used = set() + insertion_index = None + num_blank_lines = 0 + for line in lines: + if mode == START: + result.append(line) + + if re.search(REQUIRES_RE, line): + mode = SEEN_REQUIRES + + elif mode == SEEN_REQUIRES: + if (line and + not re.search(REQUIRES_RE, line) and + not line.isspace()): + # There should be two blank lines before goog.scope + result += ['\n'] * 2 + result.append('goog.scope(function() {\n') + insertion_index = len(result) + result += ['\n'] * num_blank_lines + mode = IN_SCOPE + elif line.isspace(): + # Keep track of the number of blank lines before each block of code so + # that we can move them after the goog.scope line if necessary. + num_blank_lines += 1 + else: + # Print the blank lines we saw before this code block + result += ['\n'] * num_blank_lines + num_blank_lines = 0 + result.append(line) + + if mode == IN_SCOPE: + for symbol in requires: + if not symbol in globals_to_aliases: + continue + + alias = globals_to_aliases[symbol] + matcher = aliases_to_matchers[alias] + for match in matcher.finditer(line): + # Check to make sure we're not in a string. + # We do this by being as conservative as possible: + # if there are any quote or double quote characters + # before the symbol on this line, then bail out. + before_symbol = line[:match.start(0)] + if before_symbol.count('"') > 0 or before_symbol.count("'") > 0: + continue + + line = line.replace(match.group(0), alias) + aliases_used.add(alias) + + if line.isspace(): + # Truncate all-whitespace lines + result.append('\n') + else: + result.append(line) + + if len(aliases_used): + aliases_used = [alias for alias in aliases_used] + aliases_used.sort() + aliases_used.reverse() + for alias in aliases_used: + symbol = aliases_to_globals[alias] + result.insert(insertion_index, + 'var %s = %s;\n' % (alias, symbol)) + result.append('}); // goog.scope\n') + return result + else: + return None + +def TransformFileAt(path): + """Converts a file into javascript that uses goog.scope. + + Arguments: + path: A path to a file. + """ + f = open(path) + lines = Transform(f.readlines()) + if lines: + f = open(path, 'w') + for l in lines: + f.write(l) + f.close() + +if __name__ == '__main__': + args = sys.argv[1:] + if not len(args): + args = '.' + + for file_name in args: + if os.path.isdir(file_name): + for root, dirs, files in os.walk(file_name): + for name in files: + if name.endswith('.js') and \ + not os.path.islink(os.path.join(root, name)): + TransformFileAt(os.path.join(root, name)) + else: + if file_name.endswith('.js') and \ + not os.path.islink(file_name): + TransformFileAt(file_name) diff --git a/closure-library/closure/css/inlay/g-base.css b/closure-library/closure/css/inlay/g-base.css new file mode 100644 index 0000000000..3db72d3f82 --- /dev/null +++ b/closure-library/closure/css/inlay/g-base.css @@ -0,0 +1,82 @@ +/* + * Copyright 2007 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/** + * CSS Inlay + * This is the minimum CSS required to use the markup/classname patterns. + * @author elsigh@google.com (Lindsey Simon) + * @author ddiaz@google.com (Dustin Diaz) + */ + +/** + * Document container designed for fluid width scaling. + * Alternative g-doc- fixed-width classes are in gui-fixed.css. + */ +.g-doc { + width: 100%; + text-align: left; +} + +/** + * g-section fundamentally has to clear floats. There are many ways to do this. + * This technique is nice because it doesn't rely on overflow: hidden, which + * has the potential to hide your content in situations where a fixed size + * node takes up too much space (like a big table, or a text input or image. + * Works in Webkit, IE8, and FF3. + */ +.g-section { + width: 100%; + vertical-align: top; + display: inline-block; +} + +/** + * IE7-only hack. Nicely IE7 will clear floats with just block display + * and hasLayout. + */ +*:first-child+html .g-section { + display: block; +} + +/** + * IE6 cannot hang with overflow: visible. If we use the IE7 display block + * trick in IE6 we get severe float drop in nested grids. + */ +* html .g-section { + overflow: hidden; +} + +/* FF2 can't actually hang with overflow: visible. */ +@-moz-document url-prefix() { + .g-section { + overflow: hidden; + } +} + +/** + * FF3 now needs to be reset after the previous block which affects it as well. + * We target the tt element in this hack because no one uses it. + */ +@-moz-document url-prefix() { + .g-section,tt:default { + overflow: visible; + } +} + +/* Forces "hasLayout" fixing a gamut of bugs in <= IE7. */ +.g-section, +.g-unit { + zoom: 1; +} + +/* Used for splitting a template's units text-alignment to the outer edges. */ +.g-split .g-unit { + text-align: right; +} +.g-split .g-first { + text-align: left; +} diff --git a/closure-library/closure/css/inlay/g-fixed.css b/closure-library/closure/css/inlay/g-fixed.css new file mode 100644 index 0000000000..52ef5d160a --- /dev/null +++ b/closure-library/closure/css/inlay/g-fixed.css @@ -0,0 +1,173 @@ +/* + * Copyright 2007 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/** + * CSS Inlay + * Fixed templates + * @author ddiaz@google.com (Dustin Diaz) + * @author elsigh@google.com (Lindsey Simon) + * @fileoverview + * The nature of these templates is to have one unit be a fixed width + * and the supplementary unit to take up the rest of the width + * of its parents' container. + * + * Sample Usage: +
+
+

+ Lorem Ipsum... +

+
+
+

+ Lorem Ipsum... +

+
+
+ */ + +/* Document container designed for 1024x768 */ +/* TODO(ux-webdev): convert this to a straight px value - em assumes reset. */ +.g-doc-1024 { + width: 73.074em; + min-width: 950px; /* min-width doesn't work in IE6 */ + margin: 0 auto; + text-align: left; +} +/* IE 6 */ +* html .g-doc-1024 { + width: 71.313em; +} +/* IE 7 */ +*+html .g-doc-1024 { + width: 71.313em; +} + +/* Document container designed for 800x600 */ +/* TODO(ux-webdev): convert this to a straight px value - em assumes reset. */ +.g-doc-800 { + width: 57.69em; + min-width: 750px; /* min-width doesn't work in IE6 */ + margin: 0 auto; + text-align: left; +} +/* IE 6 */ +* html .g-doc-800 { + width: 56.3em; +} +/* IE 7 */ +*+html .g-doc-800 { + width: 56.3em; +} + +/* 160px */ +.g-tpl-160 .g-unit, +.g-unit .g-tpl-160 .g-unit, +.g-unit .g-unit .g-tpl-160 .g-unit, +.g-unit .g-unit .g-unit .g-tpl-160 .g-unit { + margin: 0 0 0 160px; + width: auto; + float: none; +} +.g-unit .g-unit .g-unit .g-tpl-160 .g-first, +.g-unit .g-unit .g-tpl-160 .g-first, +.g-unit .g-tpl-160 .g-first, +.g-tpl-160 .g-first { + margin: 0; + width: 160px; + float: left; +} + +/* 160px alt */ +.g-tpl-160-alt .g-unit, +.g-unit .g-tpl-160-alt .g-unit, +.g-unit .g-unit .g-tpl-160-alt .g-unit, +.g-unit .g-unit .g-unit .g-tpl-160-alt .g-unit { + margin: 0 160px 0 0; + width: auto; + float: none; +} +.g-unit .g-unit .g-unit .g-tpl-160-alt .g-first, +.g-unit .g-unit .g-tpl-160-alt .g-first, +.g-unit .g-tpl-160-alt .g-first, +.g-tpl-160-alt .g-first { + margin: 0; + width: 160px; + float: right; +} + +/* 180px */ +.g-tpl-180 .g-unit, +.g-unit .g-tpl-180 .g-unit, +.g-unit .g-unit .g-tpl-180 .g-unit, +.g-unit .g-unit .g-unit .g-tpl-180 .g-unit { + margin: 0 0 0 180px; + width: auto; + float: none; +} +.g-unit .g-unit .g-unit .g-tpl-180 .g-first, +.g-unit .g-unit .g-tpl-180 .g-first, +.g-unit .g-tpl-180 .g-first, +.g-tpl-180 .g-first { + margin: 0; + width: 180px; + float: left; +} + +/* 180px alt */ +.g-tpl-180-alt .g-unit, +.g-unit .g-tpl-180-alt .g-unit, +.g-unit .g-unit .g-tpl-180-alt .g-unit, +.g-unit .g-unit .g-unit .g-tpl-180-alt .g-unit { + margin: 0 180px 0 0; + width: auto; + float: none; +} +.g-unit .g-unit .g-unit .g-tpl-180-alt .g-first, +.g-unit .g-unit .g-tpl-180-alt .g-first, +.g-unit .g-tpl-180-alt .g-first, +.g-tpl-180-alt .g-first { + margin: 0; + width: 180px; + float: right; +} + +/* 300px */ +.g-tpl-300 .g-unit, +.g-unit .g-tpl-300 .g-unit, +.g-unit .g-unit .g-tpl-300 .g-unit, +.g-unit .g-unit .g-unit .g-tpl-300 .g-unit { + margin: 0 0 0 300px; + width: auto; + float: none; +} +.g-unit .g-unit .g-unit .g-tpl-300 .g-first, +.g-unit .g-unit .g-tpl-300 .g-first, +.g-unit .g-tpl-300 .g-first, +.g-tpl-300 .g-first { + margin: 0; + width: 300px; + float: left; +} + +/* 300px alt */ +.g-tpl-300-alt .g-unit, +.g-unit .g-tpl-300-alt .g-unit, +.g-unit .g-unit .g-tpl-300-alt .g-unit, +.g-unit .g-unit .g-unit .g-tpl-300-alt .g-unit { + margin: 0 300px 0 0; + width: auto; + float: none; +} +.g-unit .g-unit .g-unit .g-tpl-300-alt .g-first, +.g-unit .g-unit .g-tpl-300-alt .g-first, +.g-unit .g-tpl-300-alt .g-first, +.g-tpl-300-alt .g-first { + margin: 0; + width: 300px; + float: right; +} \ No newline at end of file diff --git a/closure-library/closure/css/inlay/g-ratio.css b/closure-library/closure/css/inlay/g-ratio.css new file mode 100644 index 0000000000..6017ec0863 --- /dev/null +++ b/closure-library/closure/css/inlay/g-ratio.css @@ -0,0 +1,253 @@ +/* + * Copyright 2007 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/** + * CSS Inlay + * Percentage based templates + * @author ddiaz@google.com (Dustin Diaz) + * @author elsigh@google.com (Lindsey Simon) + * @fileoverview + * The first ten templates are described using the following convention: + * tpl-LEFT%-RIGHT% and tpl-LEFT%-RIGHT%-alt, where alt switches render order. + * + * The rationale for the percentage values are pretty fascinating. + * Three nine's are needed for a miminal affordance in the gap between units + * for Opera, while two for IE 5.5 and down. + * Straight percentages seem to work fine otherwise, but the values here test + * well cross-browser. + * + * Sample Usage: +
+
+

+ Lorem Ipsum... +

+
+
+

+ Lorem Ipsum... +

+
+
+ */ + +/* 25/75 */ +.g-tpl-25-75 .g-unit, +.g-unit .g-tpl-25-75 .g-unit, +.g-unit .g-unit .g-tpl-25-75 .g-unit, +.g-unit .g-unit .g-unit .g-tpl-25-75 .g-unit { + width: 74.999%; + float: right; + margin: 0; +} +.g-unit .g-unit .g-unit .g-tpl-25-75 .g-first, +.g-unit .g-unit .g-tpl-25-75 .g-first, +.g-unit .g-tpl-25-75 .g-first, +.g-tpl-25-75 .g-first { + width: 24.999%; + float: left; + margin: 0; +} + +/* 25/75-alt */ +.g-tpl-25-75-alt .g-unit, +.g-unit .g-tpl-25-75-alt .g-unit, +.g-unit .g-unit .g-tpl-25-75-alt .g-unit, +.g-unit .g-unit .g-unit .g-tpl-25-75-alt .g-unit { + width: 24.999%; + float: left; + margin: 0; +} +.g-unit .g-unit .g-unit .g-tpl-25-75-alt .g-first, +.g-unit .g-unit .g-tpl-25-75-alt .g-first, +.g-unit .g-tpl-25-75-alt .g-first, +.g-tpl-25-75-alt .g-first { + width: 74.999%; + float: right; + margin: 0; +} + +/* 75/25 */ +.g-tpl-75-25 .g-unit, +.g-unit .g-tpl-75-25 .g-unit, +.g-unit .g-unit .g-tpl-75-25 .g-unit, +.g-unit .g-unit .g-unit .g-tpl-75-25 .g-unit { + width: 24.999%; + float: right; + margin: 0; +} +.g-unit .g-unit .g-unit .g-tpl-75-25 .g-first, +.g-unit .g-unit .g-tpl-75-25 .g-first, +.g-unit .g-tpl-75-25 .g-first, +.g-tpl-75-25 .g-first { + width: 74.999%; + float: left; + margin: 0; +} + +/* 75/25-alt */ +.g-tpl-75-25-alt .g-unit, +.g-unit .g-tpl-75-25-alt .g-unit, +.g-unit .g-unit .g-tpl-75-25-alt .g-unit, +.g-unit .g-unit .g-unit .g-tpl-75-25-alt .g-unit { + width: 74.999%; + float: left; + margin: 0; +} +.g-unit .g-unit .g-unit .g-tpl-75-25-alt .g-first, +.g-unit .g-unit .g-tpl-75-25-alt .g-first, +.g-unit .g-tpl-75-25-alt .g-first, +.g-tpl-75-25-alt .g-first { + width: 24.999%; + float: right; + margin: 0; +} + +/* 33/67 */ +.g-tpl-33-67 .g-unit, +.g-unit .g-tpl-33-67 .g-unit, +.g-unit .g-unit .g-tpl-33-67 .g-unit, +.g-unit .g-unit .g-unit .g-tpl-33-67 .g-unit { + width: 66.999%; + float: right; + margin: 0; +} +.g-unit .g-unit .g-unit .g-tpl-33-67 .g-first, +.g-unit .g-unit .g-tpl-33-67 .g-first, +.g-unit .g-tpl-33-67 .g-first, +.g-tpl-33-67 .g-first { + width: 32.999%; + float: left; + margin: 0; +} + +/* 33/67-alt */ +.g-tpl-33-67-alt .g-unit, +.g-unit .g-tpl-33-67-alt .g-unit, +.g-unit .g-unit .g-tpl-33-67-alt .g-unit, +.g-unit .g-unit .g-unit .g-tpl-33-67-alt .g-unit { + width: 32.999%; + float: left; + margin: 0; +} +.g-unit .g-unit .g-unit .g-tpl-33-67-alt .g-first, +.g-unit .g-unit .g-tpl-33-67-alt .g-first, +.g-unit .g-tpl-33-67-alt .g-first, +.g-tpl-33-67-alt .g-first { + width: 66.999%; + float: right; + margin: 0; +} +/* 67/33 */ +.g-tpl-67-33 .g-unit, +.g-unit .g-tpl-67-33 .g-unit, +.g-unit .g-unit .g-tpl-67-33 .g-unit, +.g-unit .g-unit .g-unit .g-tpl-67-33 .g-unit { + width: 32.999%; + float: right; + margin: 0; +} +.g-unit .g-unit .g-unit .g-tpl-67-33 .g-first, +.g-unit .g-unit .g-tpl-67-33 .g-first, +.g-unit .g-tpl-67-33 .g-first, +.g-tpl-67-33 .g-first { + width: 66.999%; + float: left; + margin: 0; +} + +/* 67/33-alt */ +.g-tpl-67-33-alt .g-unit, +.g-unit .g-tpl-67-33-alt .g-unit, +.g-unit .g-unit .g-tpl-67-33-alt .g-unit, +.g-unit .g-unit .g-unit .g-tpl-67-33-alt .g-unit { + width: 66.999%; + float: left; + margin: 0; +} +.g-unit .g-unit .g-unit .g-tpl-67-33-alt .g-first, +.g-unit .g-unit .g-tpl-67-33-alt .g-first, +.g-unit .g-tpl-67-33-alt .g-first, +.g-tpl-67-33-alt .g-first { + width: 32.999%; + float: right; + margin: 0; +} + +/* 50/50 */ +.g-tpl-50-50 .g-unit, +.g-unit .g-tpl-50-50 .g-unit, +.g-unit .g-unit .g-tpl-50-50 .g-unit, +.g-unit .g-unit .g-unit .g-tpl-50-50 .g-unit { + width: 49.999%; + float: right; + margin: 0; +} +.g-unit .g-unit .g-unit .g-tpl-50-50 .g-first, +.g-unit .g-unit .g-tpl-50-50 .g-first, +.g-unit .g-tpl-50-50 .g-first, +.g-tpl-50-50 .g-first { + width: 49.999%; + float: left; + margin: 0; +} + +/* 50/50-alt */ +.g-tpl-50-50-alt .g-unit, +.g-unit .g-tpl-50-50-alt .g-unit, +.g-unit .g-unit .g-tpl-50-50-alt .g-unit, +.g-unit .g-unit .g-unit .g-tpl-50-50-alt .g-unit { + width: 49.999%; + float: left; + margin: 0; +} +.g-unit .g-unit .g-unit .g-tpl-50-50-alt .g-first, +.g-unit .g-unit .g-tpl-50-50-alt .g-first, +.g-unit .g-tpl-50-50-alt .g-first, +.g-tpl-50-50-alt .g-first { + width: 49.999%; + float: right; + margin: 0; +} + +/** + * Nest templates contain floating g-units. + * For these, width needs to be reset from the 100% for inline-block + * to auto. This fixes an issue with horizontal scrollbars. + */ +.g-tpl-nest { + width: auto; +} +/** + * Making any g-sections inside of g-tpl-nests display inline instead + * of display block solves an issue where inner sections add up incrementally + * their widths to set the width of the outer g-unit. This causes all kinds of + * problems with float-drops and display:inline fixes this. + */ +.g-tpl-nest .g-section { + display: inline; +} + +/* g-tpl-nest for multi unit nesting (float left), say for a menu. */ +.g-tpl-nest .g-unit, +.g-unit .g-tpl-nest .g-unit, +.g-unit .g-unit .g-tpl-nest .g-unit, +.g-unit .g-unit .g-unit .g-tpl-nest .g-unit { + float: left; + width: auto; + margin: 0; +} + +/* g-tpl-nest-alt for multi unit nesting (float right), say for a menu. */ +.g-tpl-nest-alt .g-unit, +.g-unit .g-tpl-nest-alt .g-unit, +.g-unit .g-unit .g-tpl-nest-alt .g-unit, +.g-unit .g-unit .g-unit .g-tpl-nest-alt .g-unit { + float: right; + width: auto; + margin: 0; +} diff --git a/closure-library/closure/goog/a11y/aria/announcer.js b/closure-library/closure/goog/a11y/aria/announcer.js new file mode 100644 index 0000000000..a0bbb6bdf0 --- /dev/null +++ b/closure-library/closure/goog/a11y/aria/announcer.js @@ -0,0 +1,121 @@ +// Copyright 2007 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 Announcer that allows messages to be spoken by assistive + * technologies. + */ + +goog.provide('goog.a11y.aria.Announcer'); + +goog.require('goog.Disposable'); +goog.require('goog.Timer'); +goog.require('goog.a11y.aria'); +goog.require('goog.a11y.aria.LivePriority'); +goog.require('goog.a11y.aria.State'); +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.object'); + + + +/** + * Class that allows messages to be spoken by assistive technologies that the + * user may have active. + * + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper. + * @constructor + * @extends {goog.Disposable} + * @final + */ +goog.a11y.aria.Announcer = function(opt_domHelper) { + goog.a11y.aria.Announcer.base(this, 'constructor'); + + /** + * @type {goog.dom.DomHelper} + * @private + */ + this.domHelper_ = opt_domHelper || goog.dom.getDomHelper(); + + /** + * Map of priority to live region elements to use for communicating updates. + * Elements are created on demand. + * @type {Object} + * @private + */ + this.liveRegions_ = {}; +}; +goog.inherits(goog.a11y.aria.Announcer, goog.Disposable); + + +/** @override */ +goog.a11y.aria.Announcer.prototype.disposeInternal = function() { + goog.object.forEach( + this.liveRegions_, this.domHelper_.removeNode, this.domHelper_); + this.liveRegions_ = null; + this.domHelper_ = null; + goog.a11y.aria.Announcer.base(this, 'disposeInternal'); +}; + + +/** + * Announce a message to be read by any assistive technologies the user may + * have active. + * @param {string} message The message to announce to screen readers. + * @param {goog.a11y.aria.LivePriority=} opt_priority The priority of the + * message. Defaults to POLITE. + */ +goog.a11y.aria.Announcer.prototype.say = function(message, opt_priority) { + var priority = opt_priority || goog.a11y.aria.LivePriority.POLITE; + var liveRegion = this.getLiveRegion_(priority); + // Resets text content to force a DOM mutation (so that the setTextContent + // post-timeout function will be noticed by the screen reader). This is to + // avoid the problem of when the same message is "said" twice, which doesn't + // trigger a DOM mutation. + goog.dom.setTextContent(liveRegion, ''); + // Uses non-zero timer to make VoiceOver and NVDA work + goog.Timer.callOnce(function() { + goog.dom.setTextContent(liveRegion, message); + }, 1); +}; + + +/** + * Returns an aria-live region that can be used to communicate announcements. + * @param {!goog.a11y.aria.LivePriority} priority The required priority. + * @return {!Element} A live region of the requested priority. + * @private + */ +goog.a11y.aria.Announcer.prototype.getLiveRegion_ = function(priority) { + var liveRegion = this.liveRegions_[priority]; + if (liveRegion) { + // Make sure the live region is not aria-hidden. + goog.a11y.aria.removeState(liveRegion, goog.a11y.aria.State.HIDDEN); + return liveRegion; + } + + liveRegion = this.domHelper_.createElement(goog.dom.TagName.DIV); + // Note that IE has a habit of declaring things that aren't display:none as + // invisible to third-party tools like JAWs, so we can't just use height:0. + liveRegion.style.position = 'absolute'; + liveRegion.style.top = '-1000px'; + liveRegion.style.height = '1px'; + liveRegion.style.overflow = 'hidden'; + goog.a11y.aria.setState(liveRegion, goog.a11y.aria.State.LIVE, priority); + goog.a11y.aria.setState(liveRegion, goog.a11y.aria.State.ATOMIC, 'true'); + this.domHelper_.getDocument().body.appendChild(liveRegion); + this.liveRegions_[priority] = liveRegion; + return liveRegion; +}; diff --git a/closure-library/closure/goog/a11y/aria/aria.js b/closure-library/closure/goog/a11y/aria/aria.js new file mode 100644 index 0000000000..067a29825a --- /dev/null +++ b/closure-library/closure/goog/a11y/aria/aria.js @@ -0,0 +1,423 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/** + * @fileoverview Utilities for adding, removing and setting ARIA roles and + * states as defined by W3C ARIA standard: http://www.w3.org/TR/wai-aria/ + * All modern browsers have some form of ARIA support, so no browser checks are + * performed when adding ARIA to components. + * + */ + +goog.provide('goog.a11y.aria'); + +goog.require('goog.a11y.aria.Role'); +goog.require('goog.a11y.aria.State'); +goog.require('goog.a11y.aria.datatables'); +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.object'); +goog.require('goog.string'); + + +/** + * ARIA states/properties prefix. + * @private + */ +goog.a11y.aria.ARIA_PREFIX_ = 'aria-'; + + +/** + * ARIA role attribute. + * @private + */ +goog.a11y.aria.ROLE_ATTRIBUTE_ = 'role'; + + +/** + * A list of tag names for which we don't need to set ARIA role and states + * because they have well supported semantics for screen readers or because + * they don't contain content to be made accessible. + * @private + */ +goog.a11y.aria.TAGS_WITH_ASSUMED_ROLES_ = goog.object.createSet([ + goog.dom.TagName.A, goog.dom.TagName.AREA, goog.dom.TagName.BUTTON, + goog.dom.TagName.HEAD, goog.dom.TagName.INPUT, goog.dom.TagName.LINK, + goog.dom.TagName.MENU, goog.dom.TagName.META, goog.dom.TagName.OPTGROUP, + goog.dom.TagName.OPTION, goog.dom.TagName.PROGRESS, goog.dom.TagName.STYLE, + goog.dom.TagName.SELECT, goog.dom.TagName.SOURCE, goog.dom.TagName.TEXTAREA, + goog.dom.TagName.TITLE, goog.dom.TagName.TRACK +]); + + +/** + * A list of roles which are considered container roles. + * Container roles are ARIA roles which use the aria-activedescendant property + * to manage their active descendants or children. See + * {@link http://www.w3.org/TR/wai-aria/states_and_properties + * #aria-activedescendant} for more information. + * @private @const {!Array} + */ +goog.a11y.aria.CONTAINER_ROLES_ = [ + goog.a11y.aria.Role.COMBOBOX, goog.a11y.aria.Role.GRID, + goog.a11y.aria.Role.GROUP, goog.a11y.aria.Role.LISTBOX, + goog.a11y.aria.Role.MENU, goog.a11y.aria.Role.MENUBAR, + goog.a11y.aria.Role.RADIOGROUP, goog.a11y.aria.Role.ROW, + goog.a11y.aria.Role.ROWGROUP, goog.a11y.aria.Role.TAB_LIST, + goog.a11y.aria.Role.TEXTBOX, goog.a11y.aria.Role.TOOLBAR, + goog.a11y.aria.Role.TREE, goog.a11y.aria.Role.TREEGRID +]; + + +/** + * Sets the role of an element. If the roleName is + * empty string or null, the role for the element is removed. + * We encourage clients to call the goog.a11y.aria.removeRole + * method instead of setting null and empty string values. + * Special handling for this case is added to ensure + * backword compatibility with existing code. + * + * @param {!Element} element DOM node to set role of. + * @param {!goog.a11y.aria.Role|string} roleName role name(s). + */ +goog.a11y.aria.setRole = function(element, roleName) { + if (!roleName) { + // Setting the ARIA role to empty string is not allowed + // by the ARIA standard. + goog.a11y.aria.removeRole(element); + } else { + if (goog.asserts.ENABLE_ASSERTS) { + goog.asserts.assert( + goog.object.containsValue(goog.a11y.aria.Role, roleName), + 'No such ARIA role ' + roleName); + } + element.setAttribute(goog.a11y.aria.ROLE_ATTRIBUTE_, roleName); + } +}; + + +/** + * Gets role of an element. + * @param {!Element} element DOM element to get role of. + * @return {?goog.a11y.aria.Role} ARIA Role name. + */ +goog.a11y.aria.getRole = function(element) { + var role = element.getAttribute(goog.a11y.aria.ROLE_ATTRIBUTE_); + return /** @type {goog.a11y.aria.Role} */ (role) || null; +}; + + +/** + * Removes role of an element. + * @param {!Element} element DOM element to remove the role from. + */ +goog.a11y.aria.removeRole = function(element) { + element.removeAttribute(goog.a11y.aria.ROLE_ATTRIBUTE_); +}; + + +/** + * Sets the state or property of an element. + * @param {!Element} element DOM node where we set state. + * @param {!(goog.a11y.aria.State|string)} stateName State attribute being set. + * Automatically adds prefix 'aria-' to the state name if the attribute is + * not an extra attribute. + * @param {string|boolean|number|!Array} value Value + * for the state attribute. + */ +goog.a11y.aria.setState = function(element, stateName, value) { + if (goog.isArray(value)) { + value = value.join(' '); + } + var attrStateName = goog.a11y.aria.getAriaAttributeName_(stateName); + if (value === '' || value == undefined) { + var defaultValueMap = goog.a11y.aria.datatables.getDefaultValuesMap(); + // Work around for browsers that don't properly support ARIA. + // According to the ARIA W3C standard, user agents should allow + // setting empty value which results in setting the default value + // for the ARIA state if such exists. The exact text from the ARIA W3C + // standard (http://www.w3.org/TR/wai-aria/states_and_properties): + // "When a value is indicated as the default, the user agent + // MUST follow the behavior prescribed by this value when the state or + // property is empty or undefined." + // The defaultValueMap contains the default values for the ARIA states + // and has as a key the goog.a11y.aria.State constant for the state. + if (stateName in defaultValueMap) { + element.setAttribute(attrStateName, defaultValueMap[stateName]); + } else { + element.removeAttribute(attrStateName); + } + } else { + element.setAttribute(attrStateName, value); + } +}; + + +/** + * Toggles the ARIA attribute of an element. + * Meant for attributes with a true/false value, but works with any attribute. + * If the attribute does not have a true/false value, the following rules apply: + * A not empty attribute will be removed. + * An empty attribute will be set to true. + * @param {!Element} el DOM node for which to set attribute. + * @param {!(goog.a11y.aria.State|string)} attr ARIA attribute being set. + * Automatically adds prefix 'aria-' to the attribute name if the attribute + * is not an extra attribute. + */ +goog.a11y.aria.toggleState = function(el, attr) { + var val = goog.a11y.aria.getState(el, attr); + if (!goog.string.isEmptyOrWhitespace(goog.string.makeSafe(val)) && + !(val == 'true' || val == 'false')) { + goog.a11y.aria.removeState(el, /** @type {!goog.a11y.aria.State} */ (attr)); + return; + } + goog.a11y.aria.setState(el, attr, val == 'true' ? 'false' : 'true'); +}; + + +/** + * Remove the state or property for the element. + * @param {!Element} element DOM node where we set state. + * @param {!goog.a11y.aria.State} stateName State name. + */ +goog.a11y.aria.removeState = function(element, stateName) { + element.removeAttribute(goog.a11y.aria.getAriaAttributeName_(stateName)); +}; + + +/** + * Gets value of specified state or property. + * @param {!Element} element DOM node to get state from. + * @param {!goog.a11y.aria.State|string} stateName State name. + * @return {string} Value of the state attribute. + */ +goog.a11y.aria.getState = function(element, stateName) { + // TODO(user): return properly typed value result -- + // boolean, number, string, null. We should be able to chain + // getState(...) and setState(...) methods. + + var attr = + /** @type {string|number|boolean} */ ( + element.getAttribute( + goog.a11y.aria.getAriaAttributeName_(stateName))); + var isNullOrUndefined = attr == null || attr == undefined; + return isNullOrUndefined ? '' : String(attr); +}; + + +/** + * Returns the activedescendant element for the input element by + * using the activedescendant ARIA property of the given element. + * @param {!Element} element DOM node to get activedescendant + * element for. + * @return {?Element} DOM node of the activedescendant, if found. + */ +goog.a11y.aria.getActiveDescendant = function(element) { + var id = + goog.a11y.aria.getState(element, goog.a11y.aria.State.ACTIVEDESCENDANT); + return goog.dom.getOwnerDocument(element).getElementById(id); +}; + + +/** + * Sets the activedescendant ARIA property value for an element. + * If the activeElement is not null, it should have an id set. + * @param {!Element} element DOM node to set activedescendant ARIA property to. + * @param {?Element} activeElement DOM node being set as activedescendant. + */ +goog.a11y.aria.setActiveDescendant = function(element, activeElement) { + var id = ''; + if (activeElement) { + id = activeElement.id; + goog.asserts.assert(id, 'The active element should have an id.'); + } + + goog.a11y.aria.setState(element, goog.a11y.aria.State.ACTIVEDESCENDANT, id); +}; + + +/** + * Gets the label of the given element. + * @param {!Element} element DOM node to get label from. + * @return {string} label The label. + */ +goog.a11y.aria.getLabel = function(element) { + return goog.a11y.aria.getState(element, goog.a11y.aria.State.LABEL); +}; + + +/** + * Sets the label of the given element. + * @param {!Element} element DOM node to set label to. + * @param {string} label The label to set. + */ +goog.a11y.aria.setLabel = function(element, label) { + goog.a11y.aria.setState(element, goog.a11y.aria.State.LABEL, label); +}; + + +/** + * Asserts that the element has a role set if it's not an HTML element whose + * semantics is well supported by most screen readers. + * Only to be used internally by the ARIA library in goog.a11y.aria.*. + * @param {!Element} element The element to assert an ARIA role set. + * @param {!IArrayLike} allowedRoles The child roles of + * the roles. + */ +goog.a11y.aria.assertRoleIsSetInternalUtil = function(element, allowedRoles) { + if (goog.a11y.aria.TAGS_WITH_ASSUMED_ROLES_[element.tagName]) { + return; + } + var elementRole = /** @type {string}*/ (goog.a11y.aria.getRole(element)); + goog.asserts.assert( + elementRole != null, 'The element ARIA role cannot be null.'); + + goog.asserts.assert( + goog.array.contains(allowedRoles, elementRole), + 'Non existing or incorrect role set for element.' + + 'The role set is "' + elementRole + '". The role should be any of "' + + allowedRoles + '". Check the ARIA specification for more details ' + + 'http://www.w3.org/TR/wai-aria/roles.'); +}; + + +/** + * Gets the boolean value of an ARIA state/property. + * @param {!Element} element The element to get the ARIA state for. + * @param {!goog.a11y.aria.State|string} stateName the ARIA state name. + * @return {?boolean} Boolean value for the ARIA state value or null if + * the state value is not 'true', not 'false', or not set. + */ +goog.a11y.aria.getStateBoolean = function(element, stateName) { + var attr = + /** @type {string|boolean|null} */ (element.getAttribute( + goog.a11y.aria.getAriaAttributeName_(stateName))); + goog.asserts.assert( + goog.isBoolean(attr) || attr == null || attr == 'true' || + attr == 'false'); + if (attr == null) { + return attr; + } + return goog.isBoolean(attr) ? attr : attr == 'true'; +}; + + +/** + * Gets the number value of an ARIA state/property. + * @param {!Element} element The element to get the ARIA state for. + * @param {!goog.a11y.aria.State|string} stateName the ARIA state name. + * @return {?number} Number value for the ARIA state value or null if + * the state value is not a number or not set. + */ +goog.a11y.aria.getStateNumber = function(element, stateName) { + var attr = + /** @type {string|number} */ ( + element.getAttribute( + goog.a11y.aria.getAriaAttributeName_(stateName))); + goog.asserts.assert( + (attr == null || !isNaN(Number(attr))) && !goog.isBoolean(attr)); + return attr == null ? null : Number(attr); +}; + + +/** + * Gets the string value of an ARIA state/property. + * @param {!Element} element The element to get the ARIA state for. + * @param {!goog.a11y.aria.State|string} stateName the ARIA state name. + * @return {?string} String value for the ARIA state value or null if + * the state value is empty string or not set. + */ +goog.a11y.aria.getStateString = function(element, stateName) { + var attr = + element.getAttribute(goog.a11y.aria.getAriaAttributeName_(stateName)); + goog.asserts.assert( + (attr == null || goog.isString(attr)) && + (attr == '' || isNaN(Number(attr))) && attr != 'true' && attr != 'false'); + return (attr == null || attr == '') ? null : attr; +}; + + +/** + * Gets array of strings value of the specified state or + * property for the element. + * Only to be used internally by the ARIA library in goog.a11y.aria.*. + * @param {!Element} element DOM node to get state from. + * @param {!goog.a11y.aria.State} stateName State name. + * @return {!IArrayLike} string Array + * value of the state attribute. + */ +goog.a11y.aria.getStringArrayStateInternalUtil = function(element, stateName) { + var attrValue = + element.getAttribute(goog.a11y.aria.getAriaAttributeName_(stateName)); + return goog.a11y.aria.splitStringOnWhitespace_(attrValue); +}; + + +/** + * Returns true if element has an ARIA state/property, false otherwise. + * @param {!Element} element The element to get the ARIA state for. + * @param {!goog.a11y.aria.State|string} stateName the ARIA state name. + * @return {boolean} + */ +goog.a11y.aria.hasState = function(element, stateName) { + return element.hasAttribute(goog.a11y.aria.getAriaAttributeName_(stateName)); +}; + + +/** + * Returns whether the element has a container ARIA role. + * Container roles are ARIA roles that use the aria-activedescendant property + * to manage their active descendants or children. See + * {@link http://www.w3.org/TR/wai-aria/states_and_properties + * #aria-activedescendant} for more information. + * @param {!Element} element + * @return {boolean} + */ +goog.a11y.aria.isContainerRole = function(element) { + var role = goog.a11y.aria.getRole(element); + return goog.array.contains(goog.a11y.aria.CONTAINER_ROLES_, role); +}; + + +/** + * Splits the input stringValue on whitespace. + * @param {string} stringValue The value of the string to split. + * @return {!IArrayLike} string Array + * value as result of the split. + * @private + */ +goog.a11y.aria.splitStringOnWhitespace_ = function(stringValue) { + return stringValue ? stringValue.split(/\s+/) : []; +}; + + +/** + * Adds the 'aria-' prefix to ariaName. + * @param {string} ariaName ARIA state/property name. + * @private + * @return {string} The ARIA attribute name with added 'aria-' prefix. + * @throws {Error} If no such attribute exists. + */ +goog.a11y.aria.getAriaAttributeName_ = function(ariaName) { + if (goog.asserts.ENABLE_ASSERTS) { + goog.asserts.assert(ariaName, 'ARIA attribute cannot be empty.'); + goog.asserts.assert( + goog.object.containsValue(goog.a11y.aria.State, ariaName), + 'No such ARIA attribute ' + ariaName); + } + return goog.a11y.aria.ARIA_PREFIX_ + ariaName; +}; diff --git a/closure-library/closure/goog/a11y/aria/attributes.js b/closure-library/closure/goog/a11y/aria/attributes.js new file mode 100644 index 0000000000..f1e62358b7 --- /dev/null +++ b/closure-library/closure/goog/a11y/aria/attributes.js @@ -0,0 +1,399 @@ +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/** + * @fileoverview The file contains generated enumerations for ARIA states + * and properties as defined by W3C ARIA standard: + * http://www.w3.org/TR/wai-aria/. + * + * This is auto-generated code. Do not manually edit! For more details + * about how to edit it via the generator check go/closure-ariagen. + */ + +goog.provide('goog.a11y.aria.AutoCompleteValues'); +goog.provide('goog.a11y.aria.CheckedValues'); +goog.provide('goog.a11y.aria.DropEffectValues'); +goog.provide('goog.a11y.aria.ExpandedValues'); +goog.provide('goog.a11y.aria.GrabbedValues'); +goog.provide('goog.a11y.aria.InvalidValues'); +goog.provide('goog.a11y.aria.LivePriority'); +goog.provide('goog.a11y.aria.OrientationValues'); +goog.provide('goog.a11y.aria.PressedValues'); +goog.provide('goog.a11y.aria.RelevantValues'); +goog.provide('goog.a11y.aria.SelectedValues'); +goog.provide('goog.a11y.aria.SortValues'); +goog.provide('goog.a11y.aria.State'); + + +/** + * ARIA states and properties. + * @enum {string} + */ +goog.a11y.aria.State = { + // ARIA property for setting the currently active descendant of an element, + // for example the selected item in a list box. Value: ID of an element. + ACTIVEDESCENDANT: 'activedescendant', + + // ARIA property that, if true, indicates that all of a changed region should + // be presented, instead of only parts. Value: one of {true, false}. + ATOMIC: 'atomic', + + // ARIA property to specify that input completion is provided. Value: + // one of {'inline', 'list', 'both', 'none'}. + AUTOCOMPLETE: 'autocomplete', + + // ARIA state to indicate that an element and its subtree are being updated. + // Value: one of {true, false}. + BUSY: 'busy', + + // ARIA state for a checked item. Value: one of {'true', 'false', 'mixed', + // undefined}. + CHECKED: 'checked', + + // ARIA state that defines an element's column index or position with respect + // to the total number of columns within a table, grid, or treegrid. + // Value: number. + COLINDEX: 'colindex', + + // ARIA property that identifies the element or elements whose contents or + // presence are controlled by this element. + // Value: space-separated IDs of other elements. + CONTROLS: 'controls', + + // ARIA property that identifies the element or elements that describe + // this element. Value: space-separated IDs of other elements. + DESCRIBEDBY: 'describedby', + + // ARIA state for a disabled item. Value: one of {true, false}. + DISABLED: 'disabled', + + // ARIA property that indicates what functions can be performed when a + // dragged object is released on the drop target. Value: one of + // {'copy', 'move', 'link', 'execute', 'popup', 'none'}. + DROPEFFECT: 'dropeffect', + + // ARIA state for setting whether the element like a tree node is expanded. + // Value: one of {true, false, undefined}. + EXPANDED: 'expanded', + + // ARIA property that identifies the next element (or elements) in the + // recommended reading order of content. Value: space-separated ids of + // elements to flow to. + FLOWTO: 'flowto', + + // ARIA state that indicates an element's "grabbed" state in drag-and-drop. + // Value: one of {true, false, undefined}. + GRABBED: 'grabbed', + + // ARIA property indicating whether the element has a popup. + // Value: one of {true, false}. + HASPOPUP: 'haspopup', + + // ARIA state indicating that the element is not visible or perceivable + // to any user. Value: one of {true, false}. + HIDDEN: 'hidden', + + // ARIA state indicating that the entered value does not conform. Value: + // one of {false, true, 'grammar', 'spelling'} + INVALID: 'invalid', + + // ARIA property that provides a label to override any other text, value, or + // contents used to describe this element. Value: string. + LABEL: 'label', + + // ARIA property for setting the element which labels another element. + // Value: space-separated IDs of elements. + LABELLEDBY: 'labelledby', + + // ARIA property for setting the level of an element in the hierarchy. + // Value: integer. + LEVEL: 'level', + + // ARIA property indicating that an element will be updated, and + // describes the types of updates the user agents, assistive technologies, + // and user can expect from the live region. Value: one of {'off', 'polite', + // 'assertive'}. + LIVE: 'live', + + // ARIA property indicating whether a text box can accept multiline input. + // Value: one of {true, false}. + MULTILINE: 'multiline', + + // ARIA property indicating if the user may select more than one item. + // Value: one of {true, false}. + MULTISELECTABLE: 'multiselectable', + + // ARIA property indicating if the element is horizontal or vertical. + // Value: one of {'vertical', 'horizontal'}. + ORIENTATION: 'orientation', + + // ARIA property creating a visual, functional, or contextual parent/child + // relationship when the DOM hierarchy can't be used to represent it. + // Value: Space-separated IDs of elements. + OWNS: 'owns', + + // ARIA property that defines an element's number of position in a list. + // Value: integer. + POSINSET: 'posinset', + + // ARIA state for a pressed item. + // Value: one of {true, false, undefined, 'mixed'}. + PRESSED: 'pressed', + + // ARIA property indicating that an element is not editable. + // Value: one of {true, false}. + READONLY: 'readonly', + + // ARIA property indicating that change notifications within this subtree + // of a live region should be announced. Value: one of {'additions', + // 'removals', 'text', 'all', 'additions text'}. + RELEVANT: 'relevant', + + // ARIA property indicating that user input is required on this element + // before a form may be submitted. Value: one of {true, false}. + REQUIRED: 'required', + + // ARIA state that defines an element's row index or position with respect + // to the total number of rows within a table, grid, or treegrid. + // Value: number. + ROWINDEX: 'rowindex', + + // ARIA state for setting the currently selected item in the list. + // Value: one of {true, false, undefined}. + SELECTED: 'selected', + + // ARIA property defining the number of items in a list. Value: integer. + SETSIZE: 'setsize', + + // ARIA property indicating if items are sorted. Value: one of {'ascending', + // 'descending', 'none', 'other'}. + SORT: 'sort', + + // ARIA property for slider maximum value. Value: number. + VALUEMAX: 'valuemax', + + // ARIA property for slider minimum value. Value: number. + VALUEMIN: 'valuemin', + + // ARIA property for slider active value. Value: number. + VALUENOW: 'valuenow', + + // ARIA property for slider active value represented as text. + // Value: string. + VALUETEXT: 'valuetext' +}; + + +/** + * ARIA state values for AutoCompleteValues. + * @enum {string} + */ +goog.a11y.aria.AutoCompleteValues = { + // The system provides text after the caret as a suggestion + // for how to complete the field. + INLINE: 'inline', + // A list of choices appears from which the user can choose, + // but the edit box retains focus. + LIST: 'list', + // A list of choices appears and the currently selected suggestion + // also appears inline. + BOTH: 'both', + // No input completion suggestions are provided. + NONE: 'none' +}; + + +/** + * ARIA state values for DropEffectValues. + * @enum {string} + */ +goog.a11y.aria.DropEffectValues = { + // A duplicate of the source object will be dropped into the target. + COPY: 'copy', + // The source object will be removed from its current location + // and dropped into the target. + MOVE: 'move', + // A reference or shortcut to the dragged object + // will be created in the target object. + LINK: 'link', + // A function supported by the drop target is + // executed, using the drag source as an input. + EXECUTE: 'execute', + // There is a popup menu or dialog that allows the user to choose + // one of the drag operations (copy, move, link, execute) and any other + // drag functionality, such as cancel. + POPUP: 'popup', + // No operation can be performed; effectively + // cancels the drag operation if an attempt is made to drop on this object. + NONE: 'none' +}; + + +/** + * ARIA state values for LivePriority. + * @enum {string} + */ +goog.a11y.aria.LivePriority = { + // Updates to the region will not be presented to the user + // unless the assitive technology is currently focused on that region. + OFF: 'off', + // (Background change) Assistive technologies SHOULD announce + // updates at the next graceful opportunity, such as at the end of + // speaking the current sentence or when the user pauses typing. + POLITE: 'polite', + // This information has the highest priority and assistive + // technologies SHOULD notify the user immediately. + // Because an interruption may disorient users or cause them to not complete + // their current task, authors SHOULD NOT use the assertive value unless the + // interruption is imperative. + ASSERTIVE: 'assertive' +}; + + +/** + * ARIA state values for OrientationValues. + * @enum {string} + */ +goog.a11y.aria.OrientationValues = { + // The element is oriented vertically. + VERTICAL: 'vertical', + // The element is oriented horizontally. + HORIZONTAL: 'horizontal' +}; + + +/** + * ARIA state values for RelevantValues. + * @enum {string} + */ +goog.a11y.aria.RelevantValues = { + // Element nodes are added to the DOM within the live region. + ADDITIONS: 'additions', + // Text or element nodes within the live region are removed from the DOM. + REMOVALS: 'removals', + // Text is added to any DOM descendant nodes of the live region. + TEXT: 'text', + // Equivalent to the combination of all values, "additions removals text". + ALL: 'all' +}; + + +/** + * ARIA state values for SortValues. + * @enum {string} + */ +goog.a11y.aria.SortValues = { + // Items are sorted in ascending order by this column. + ASCENDING: 'ascending', + // Items are sorted in descending order by this column. + DESCENDING: 'descending', + // There is no defined sort applied to the column. + NONE: 'none', + // A sort algorithm other than ascending or descending has been applied. + OTHER: 'other' +}; + + +/** + * ARIA state values for CheckedValues. + * @enum {string} + */ +goog.a11y.aria.CheckedValues = { + // The selectable element is checked. + TRUE: 'true', + // The selectable element is not checked. + FALSE: 'false', + // Indicates a mixed mode value for a tri-state + // checkbox or menuitemcheckbox. + MIXED: 'mixed', + // The element does not support being checked. + UNDEFINED: 'undefined' +}; + + +/** + * ARIA state values for ExpandedValues. + * @enum {string} + */ +goog.a11y.aria.ExpandedValues = { + // The element, or another grouping element it controls, is expanded. + TRUE: 'true', + // The element, or another grouping element it controls, is collapsed. + FALSE: 'false', + // The element, or another grouping element + // it controls, is neither expandable nor collapsible; all its + // child elements are shown or there are no child elements. + UNDEFINED: 'undefined' +}; + + +/** + * ARIA state values for GrabbedValues. + * @enum {string} + */ +goog.a11y.aria.GrabbedValues = { + // Indicates that the element has been "grabbed" for dragging. + TRUE: 'true', + // Indicates that the element supports being dragged. + FALSE: 'false', + // Indicates that the element does not support being dragged. + UNDEFINED: 'undefined' +}; + + +/** + * ARIA state values for InvalidValues. + * @enum {string} + */ +goog.a11y.aria.InvalidValues = { + // There are no detected errors in the value. + FALSE: 'false', + // The value entered by the user has failed validation. + TRUE: 'true', + // A grammatical error was detected. + GRAMMAR: 'grammar', + // A spelling error was detected. + SPELLING: 'spelling' +}; + + +/** + * ARIA state values for PressedValues. + * @enum {string} + */ +goog.a11y.aria.PressedValues = { + // The element is pressed. + TRUE: 'true', + // The element supports being pressed but is not currently pressed. + FALSE: 'false', + // Indicates a mixed mode value for a tri-state toggle button. + MIXED: 'mixed', + // The element does not support being pressed. + UNDEFINED: 'undefined' +}; + + +/** + * ARIA state values for SelectedValues. + * @enum {string} + */ +goog.a11y.aria.SelectedValues = { + // The selectable element is selected. + TRUE: 'true', + // The selectable element is not selected. + FALSE: 'false', + // The element is not selectable. + UNDEFINED: 'undefined' +}; diff --git a/closure-library/closure/goog/a11y/aria/datatables.js b/closure-library/closure/goog/a11y/aria/datatables.js new file mode 100644 index 0000000000..66beaa7af5 --- /dev/null +++ b/closure-library/closure/goog/a11y/aria/datatables.js @@ -0,0 +1,63 @@ +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + + +/** + * @fileoverview The file contains data tables generated from the ARIA + * standard schema http://www.w3.org/TR/wai-aria/. + * + * This is auto-generated code. Do not manually edit! + */ + +goog.provide('goog.a11y.aria.datatables'); + +goog.require('goog.a11y.aria.State'); +goog.require('goog.object'); + + +/** + * A map that contains mapping between an ARIA state and the default value + * for it. Note that not all ARIA states have default values. + * + * @type {Object} + */ +goog.a11y.aria.DefaultStateValueMap_; + + +/** + * A method that creates a map that contains mapping between an ARIA state and + * the default value for it. Note that not all ARIA states have default values. + * + * @return {!Object} + * The names for each of the notification methods. + */ +goog.a11y.aria.datatables.getDefaultValuesMap = function() { + if (!goog.a11y.aria.DefaultStateValueMap_) { + goog.a11y.aria.DefaultStateValueMap_ = goog.object.create( + goog.a11y.aria.State.ATOMIC, false, goog.a11y.aria.State.AUTOCOMPLETE, + 'none', goog.a11y.aria.State.DROPEFFECT, 'none', + goog.a11y.aria.State.HASPOPUP, false, goog.a11y.aria.State.LIVE, 'off', + goog.a11y.aria.State.MULTILINE, false, + goog.a11y.aria.State.MULTISELECTABLE, false, + goog.a11y.aria.State.ORIENTATION, 'vertical', + goog.a11y.aria.State.READONLY, false, goog.a11y.aria.State.RELEVANT, + 'additions text', goog.a11y.aria.State.REQUIRED, false, + goog.a11y.aria.State.SORT, 'none', goog.a11y.aria.State.BUSY, false, + goog.a11y.aria.State.DISABLED, false, goog.a11y.aria.State.HIDDEN, + false, goog.a11y.aria.State.INVALID, 'false'); + } + + return goog.a11y.aria.DefaultStateValueMap_; +}; diff --git a/closure-library/closure/goog/a11y/aria/roles.js b/closure-library/closure/goog/a11y/aria/roles.js new file mode 100644 index 0000000000..1ecbc1d4b9 --- /dev/null +++ b/closure-library/closure/goog/a11y/aria/roles.js @@ -0,0 +1,219 @@ +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/** + * @fileoverview The file contains generated enumerations for ARIA roles + * as defined by W3C ARIA standard: http://www.w3.org/TR/wai-aria/. + * + * This is auto-generated code. Do not manually edit! For more details + * about how to edit it via the generator check go/closure-ariagen. + */ + +goog.provide('goog.a11y.aria.Role'); + + +/** + * ARIA role values. + * @enum {string} + */ +goog.a11y.aria.Role = { + // ARIA role for an alert element that doesn't need to be explicitly closed. + ALERT: 'alert', + + // ARIA role for an alert dialog element that takes focus and must be closed. + ALERTDIALOG: 'alertdialog', + + // ARIA role for an application that implements its own keyboard navigation. + APPLICATION: 'application', + + // ARIA role for an article. + ARTICLE: 'article', + + // ARIA role for a banner containing mostly site content, not page content. + BANNER: 'banner', + + // ARIA role for a button element. + BUTTON: 'button', + + // ARIA role for a checkbox button element; use with the CHECKED state. + CHECKBOX: 'checkbox', + + // ARIA role for a column header of a table or grid. + COLUMNHEADER: 'columnheader', + + // ARIA role for a combo box element. + COMBOBOX: 'combobox', + + // ARIA role for a supporting section of the document. + COMPLEMENTARY: 'complementary', + + // ARIA role for a large perceivable region that contains information + // about the parent document. + CONTENTINFO: 'contentinfo', + + // ARIA role for a definition of a term or concept. + DEFINITION: 'definition', + + // ARIA role for a dialog, some descendant must take initial focus. + DIALOG: 'dialog', + + // ARIA role for a directory, like a table of contents. + DIRECTORY: 'directory', + + // ARIA role for a part of a page that's a document, not a web application. + DOCUMENT: 'document', + + // ARIA role for a landmark region logically considered one form. + FORM: 'form', + + // ARIA role for an interactive control of tabular data. + GRID: 'grid', + + // ARIA role for a cell in a grid. + GRIDCELL: 'gridcell', + + // ARIA role for a group of related elements like tree item siblings. + GROUP: 'group', + + // ARIA role for a heading element. + HEADING: 'heading', + + // ARIA role for a container of elements that together comprise one image. + IMG: 'img', + + // ARIA role for a link. + LINK: 'link', + + // ARIA role for a list of non-interactive list items. + LIST: 'list', + + // ARIA role for a listbox. + LISTBOX: 'listbox', + + // ARIA role for a list item. + LISTITEM: 'listitem', + + // ARIA role for a live region where new information is added. + LOG: 'log', + + // ARIA landmark role for the main content in a document. Use only once. + MAIN: 'main', + + // ARIA role for a live region of non-essential information that changes. + MARQUEE: 'marquee', + + // ARIA role for a mathematical expression. + MATH: 'math', + + // ARIA role for a popup menu. + MENU: 'menu', + + // ARIA role for a menubar element containing menu elements. + MENUBAR: 'menubar', + + // ARIA role for menu item elements. + MENU_ITEM: 'menuitem', + + // ARIA role for a checkbox box element inside a menu. + MENU_ITEM_CHECKBOX: 'menuitemcheckbox', + + // ARIA role for a radio button element inside a menu. + MENU_ITEM_RADIO: 'menuitemradio', + + // ARIA landmark role for a collection of navigation links. + NAVIGATION: 'navigation', + + // ARIA role for a section ancillary to the main content. + NOTE: 'note', + + // ARIA role for option items that are children of combobox, listbox, menu, + // radiogroup, or tree elements. + OPTION: 'option', + + // ARIA role for ignorable cosmetic elements with no semantic significance. + PRESENTATION: 'presentation', + + // ARIA role for a progress bar element. + PROGRESSBAR: 'progressbar', + + // ARIA role for a radio button element. + RADIO: 'radio', + + // ARIA role for a group of connected radio button elements. + RADIOGROUP: 'radiogroup', + + // ARIA role for an important region of the page. + REGION: 'region', + + // ARIA role for a row of cells in a grid. + ROW: 'row', + + // ARIA role for a group of one or more rows in a grid. + ROWGROUP: 'rowgroup', + + // ARIA role for a row header of a table or grid. + ROWHEADER: 'rowheader', + + // ARIA role for a scrollbar element. + SCROLLBAR: 'scrollbar', + + // ARIA landmark role for a part of the page providing search functionality. + SEARCH: 'search', + + // ARIA role for a menu separator. + SEPARATOR: 'separator', + + // ARIA role for a slider. + SLIDER: 'slider', + + // ARIA role for a spin button. + SPINBUTTON: 'spinbutton', + + // ARIA role for a live region with advisory info less severe than an alert. + STATUS: 'status', + + // ARIA role for a tab button. + TAB: 'tab', + + // ARIA role for a tab bar (i.e. a list of tab buttons). + TAB_LIST: 'tablist', + + // ARIA role for a tab page (i.e. the element holding tab contents). + TAB_PANEL: 'tabpanel', + + // ARIA role for a textbox element. + TEXTBOX: 'textbox', + + // ARIA role for a textinfo element. + TEXTINFO: 'textinfo', + + // ARIA role for an element displaying elapsed time or time remaining. + TIMER: 'timer', + + // ARIA role for a toolbar element. + TOOLBAR: 'toolbar', + + // ARIA role for a tooltip element. + TOOLTIP: 'tooltip', + + // ARIA role for a tree. + TREE: 'tree', + + // ARIA role for a grid whose rows can be expanded and collapsed like a tree. + TREEGRID: 'treegrid', + + // ARIA role for a tree item that sometimes may be expanded or collapsed. + TREEITEM: 'treeitem' +}; diff --git a/closure-library/closure/goog/array/array.js b/closure-library/closure/goog/array/array.js new file mode 100644 index 0000000000..46f7a0d450 --- /dev/null +++ b/closure-library/closure/goog/array/array.js @@ -0,0 +1,1665 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities for manipulating arrays. + * + * @author arv@google.com (Erik Arvidsson) + */ + + +goog.provide('goog.array'); + +goog.require('goog.asserts'); + + +/** + * @define {boolean} NATIVE_ARRAY_PROTOTYPES indicates whether the code should + * rely on Array.prototype functions, if available. + * + * The Array.prototype functions can be defined by external libraries like + * Prototype and setting this flag to false forces closure to use its own + * goog.array implementation. + * + * If your javascript can be loaded by a third party site and you are wary about + * relying on the prototype functions, specify + * "--define goog.NATIVE_ARRAY_PROTOTYPES=false" to the JSCompiler. + * + * Setting goog.TRUSTED_SITE to false will automatically set + * NATIVE_ARRAY_PROTOTYPES to false. + */ +goog.define('goog.NATIVE_ARRAY_PROTOTYPES', goog.TRUSTED_SITE); + + +/** + * @define {boolean} If true, JSCompiler will use the native implementation of + * array functions where appropriate (e.g., `Array#filter`) and remove the + * unused pure JS implementation. + */ +goog.define('goog.array.ASSUME_NATIVE_FUNCTIONS', false); + + +/** + * Returns the last element in an array without removing it. + * Same as goog.array.last. + * @param {IArrayLike|string} array The array. + * @return {T} Last item in array. + * @template T + */ +goog.array.peek = function(array) { + return array[array.length - 1]; +}; + + +/** + * Returns the last element in an array without removing it. + * Same as goog.array.peek. + * @param {IArrayLike|string} array The array. + * @return {T} Last item in array. + * @template T + */ +goog.array.last = goog.array.peek; + +// NOTE(arv): Since most of the array functions are generic it allows you to +// pass an array-like object. Strings have a length and are considered array- +// like. However, the 'in' operator does not work on strings so we cannot just +// use the array path even if the browser supports indexing into strings. We +// therefore end up splitting the string. + + +/** + * Returns the index of the first element of an array with a specified value, or + * -1 if the element is not present in the array. + * + * See {@link http://tinyurl.com/developer-mozilla-org-array-indexof} + * + * @param {IArrayLike|string} arr The array to be searched. + * @param {T} obj The object for which we are searching. + * @param {number=} opt_fromIndex The index at which to start the search. If + * omitted the search starts at index 0. + * @return {number} The index of the first matching array element. + * @template T + */ +goog.array.indexOf = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.indexOf) ? + function(arr, obj, opt_fromIndex) { + goog.asserts.assert(arr.length != null); + + return Array.prototype.indexOf.call(arr, obj, opt_fromIndex); + } : + function(arr, obj, opt_fromIndex) { + var fromIndex = opt_fromIndex == null ? + 0 : + (opt_fromIndex < 0 ? Math.max(0, arr.length + opt_fromIndex) : + opt_fromIndex); + + if (goog.isString(arr)) { + // Array.prototype.indexOf uses === so only strings should be found. + if (!goog.isString(obj) || obj.length != 1) { + return -1; + } + return arr.indexOf(obj, fromIndex); + } + + for (var i = fromIndex; i < arr.length; i++) { + if (i in arr && arr[i] === obj) return i; + } + return -1; + }; + + +/** + * Returns the index of the last element of an array with a specified value, or + * -1 if the element is not present in the array. + * + * See {@link http://tinyurl.com/developer-mozilla-org-array-lastindexof} + * + * @param {!IArrayLike|string} arr The array to be searched. + * @param {T} obj The object for which we are searching. + * @param {?number=} opt_fromIndex The index at which to start the search. If + * omitted the search starts at the end of the array. + * @return {number} The index of the last matching array element. + * @template T + */ +goog.array.lastIndexOf = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.lastIndexOf) ? + function(arr, obj, opt_fromIndex) { + goog.asserts.assert(arr.length != null); + + // Firefox treats undefined and null as 0 in the fromIndex argument which + // leads it to always return -1 + var fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex; + return Array.prototype.lastIndexOf.call(arr, obj, fromIndex); + } : + function(arr, obj, opt_fromIndex) { + var fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex; + + if (fromIndex < 0) { + fromIndex = Math.max(0, arr.length + fromIndex); + } + + if (goog.isString(arr)) { + // Array.prototype.lastIndexOf uses === so only strings should be found. + if (!goog.isString(obj) || obj.length != 1) { + return -1; + } + return arr.lastIndexOf(obj, fromIndex); + } + + for (var i = fromIndex; i >= 0; i--) { + if (i in arr && arr[i] === obj) return i; + } + return -1; + }; + + +/** + * 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 {IArrayLike|string} arr Array or array like object over + * which to iterate. + * @param {?function(this: S, T, number, ?): ?} f The function to call for every + * element. This function takes 3 arguments (the element, the index and the + * array). The return value is ignored. + * @param {S=} opt_obj The object to be used as the value of 'this' within f. + * @template T,S + */ +goog.array.forEach = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.forEach) ? + function(arr, f, opt_obj) { + goog.asserts.assert(arr.length != null); + + Array.prototype.forEach.call(arr, f, opt_obj); + } : + function(arr, f, opt_obj) { + var l = arr.length; // must be fixed during loop... see docs + var arr2 = goog.isString(arr) ? arr.split('') : arr; + for (var i = 0; i < l; i++) { + if (i in arr2) { + f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr); + } + } + }; + + +/** + * Calls a function for each element in an array, starting from the last + * element rather than the first. + * + * @param {IArrayLike|string} arr Array or array + * like object over which to iterate. + * @param {?function(this: S, T, number, ?): ?} f The function to call for every + * element. This function + * takes 3 arguments (the element, the index and the array). The return + * value is ignored. + * @param {S=} opt_obj The object to be used as the value of 'this' + * within f. + * @template T,S + */ +goog.array.forEachRight = function(arr, f, opt_obj) { + var l = arr.length; // must be fixed during loop... see docs + var arr2 = goog.isString(arr) ? arr.split('') : arr; + for (var i = l - 1; i >= 0; --i) { + if (i in arr2) { + f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr); + } + } +}; + + +/** + * Calls a function for each element in an array, and if the function returns + * true adds the element to a new array. + * + * See {@link http://tinyurl.com/developer-mozilla-org-array-filter} + * + * @param {IArrayLike|string} 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 must + * return a Boolean. If the return value is true the element is added to the + * result array. If it is false the element is not included. + * @param {S=} opt_obj The object to be used as the value of 'this' + * within f. + * @return {!Array} a new array in which only elements that passed the test + * are present. + * @template T,S + */ +goog.array.filter = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.filter) ? + function(arr, f, opt_obj) { + goog.asserts.assert(arr.length != null); + + return Array.prototype.filter.call(arr, f, opt_obj); + } : + function(arr, f, opt_obj) { + var l = arr.length; // must be fixed during loop... see docs + var res = []; + var resLength = 0; + var arr2 = goog.isString(arr) ? arr.split('') : arr; + for (var i = 0; i < l; i++) { + if (i in arr2) { + var val = arr2[i]; // in case f mutates arr2 + if (f.call(/** @type {?} */ (opt_obj), val, i, arr)) { + res[resLength++] = val; + } + } + } + return res; + }; + + +/** + * Calls a function for each element in an array and inserts the result into a + * new array. + * + * See {@link http://tinyurl.com/developer-mozilla-org-array-map} + * + * @param {IArrayLike|string} arr Array or array like object + * over which to iterate. + * @param {function(this:THIS, VALUE, number, ?): RESULT} f The function to call + * for every element. This function takes 3 arguments (the element, + * the index and the array) and should return something. The result will be + * inserted into a new array. + * @param {THIS=} opt_obj The object to be used as the value of 'this' within f. + * @return {!Array} a new array with the results from f. + * @template THIS, VALUE, RESULT + */ +goog.array.map = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.map) ? + function(arr, f, opt_obj) { + goog.asserts.assert(arr.length != null); + + return Array.prototype.map.call(arr, f, opt_obj); + } : + function(arr, f, opt_obj) { + var l = arr.length; // must be fixed during loop... see docs + var res = new Array(l); + var arr2 = goog.isString(arr) ? arr.split('') : arr; + for (var i = 0; i < l; i++) { + if (i in arr2) { + res[i] = f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr); + } + } + return res; + }; + + +/** + * Passes every element of an array into a function and accumulates the result. + * + * See {@link http://tinyurl.com/developer-mozilla-org-array-reduce} + * + * For example: + * var a = [1, 2, 3, 4]; + * goog.array.reduce(a, function(r, v, i, arr) {return r + v;}, 0); + * returns 10 + * + * @param {IArrayLike|string} 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 + * 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 + * array itself) + * function(previousValue, currentValue, index, array). + * @param {?} val The initial value to pass into the function on the first call. + * @param {S=} opt_obj The object to be used as the value of 'this' + * within f. + * @return {R} Result of evaluating f repeatedly across the values of the array. + * @template T,S,R + */ +goog.array.reduce = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.reduce) ? + function(arr, f, val, opt_obj) { + goog.asserts.assert(arr.length != null); + if (opt_obj) { + f = goog.bind(f, opt_obj); + } + return Array.prototype.reduce.call(arr, f, val); + } : + function(arr, f, val, opt_obj) { + var rval = val; + goog.array.forEach(arr, function(val, index) { + rval = f.call(/** @type {?} */ (opt_obj), rval, val, index, arr); + }); + return rval; + }; + + +/** + * Passes every element of an array into a function and accumulates the result, + * starting from the last element and working towards the first. + * + * See {@link http://tinyurl.com/developer-mozilla-org-array-reduceright} + * + * For example: + * var a = ['a', 'b', 'c']; + * goog.array.reduceRight(a, function(r, v, i, arr) {return r + v;}, ''); + * returns 'cba' + * + * @param {IArrayLike|string} 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 + * 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 + * array itself) + * function(previousValue, currentValue, index, array). + * @param {?} val The initial value to pass into the function on the first call. + * @param {S=} opt_obj The object to be used as the value of 'this' + * within f. + * @return {R} Object returned as a result of evaluating f repeatedly across the + * values of the array. + * @template T,S,R + */ +goog.array.reduceRight = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.reduceRight) ? + function(arr, f, val, opt_obj) { + goog.asserts.assert(arr.length != null); + goog.asserts.assert(f != null); + if (opt_obj) { + f = goog.bind(f, opt_obj); + } + return Array.prototype.reduceRight.call(arr, f, val); + } : + function(arr, f, val, opt_obj) { + var rval = val; + goog.array.forEachRight(arr, function(val, index) { + rval = f.call(/** @type {?} */ (opt_obj), rval, val, index, arr); + }); + return rval; + }; + + +/** + * Calls f for each element of an array. If any call returns true, some() + * returns true (without checking the remaining elements). If all calls + * return false, some() returns false. + * + * See {@link http://tinyurl.com/developer-mozilla-org-array-some} + * + * @param {IArrayLike|string} 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 + * index and the array) and should return a boolean. + * @param {S=} opt_obj The object to be used as the value of 'this' + * within f. + * @return {boolean} true if any element passes the test. + * @template T,S + */ +goog.array.some = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.some) ? + function(arr, f, opt_obj) { + goog.asserts.assert(arr.length != null); + + return Array.prototype.some.call(arr, f, opt_obj); + } : + function(arr, f, opt_obj) { + var l = arr.length; // must be fixed during loop... see docs + var arr2 = goog.isString(arr) ? arr.split('') : arr; + for (var i = 0; i < l; i++) { + if (i in arr2 && f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) { + return true; + } + } + return false; + }; + + +/** + * Call f for each element of an array. If all calls return true, every() + * returns true. If any call returns false, every() returns false and + * does not continue to check the remaining elements. + * + * See {@link http://tinyurl.com/developer-mozilla-org-array-every} + * + * @param {IArrayLike|string} 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 + * index and the array) and should return a boolean. + * @param {S=} opt_obj The object to be used as the value of 'this' + * within f. + * @return {boolean} false if any element fails the test. + * @template T,S + */ +goog.array.every = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.every) ? + function(arr, f, opt_obj) { + goog.asserts.assert(arr.length != null); + + return Array.prototype.every.call(arr, f, opt_obj); + } : + function(arr, f, opt_obj) { + var l = arr.length; // must be fixed during loop... see docs + var arr2 = goog.isString(arr) ? arr.split('') : arr; + for (var i = 0; i < l; i++) { + if (i in arr2 && !f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) { + return false; + } + } + return true; + }; + + +/** + * Counts the array elements that fulfill the predicate, i.e. for which the + * callback function returns true. Skips holes in the array. + * + * @param {!IArrayLike|string} 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). + * @param {S=} opt_obj The object to be used as the value of 'this' within f. + * @return {number} The number of the matching elements. + * @template T,S + */ +goog.array.count = function(arr, f, opt_obj) { + var count = 0; + goog.array.forEach(arr, function(element, index, arr) { + if (f.call(/** @type {?} */ (opt_obj), element, index, arr)) { + ++count; + } + }, opt_obj); + return count; +}; + + +/** + * Search an array for the first element that satisfies a given condition and + * return that element. + * @param {IArrayLike|string} 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|null} The first array element that passes the test, or null if no + * element is found. + * @template T,S + */ +goog.array.find = function(arr, f, opt_obj) { + var i = goog.array.findIndex(arr, f, opt_obj); + return i < 0 ? null : goog.isString(arr) ? arr.charAt(i) : arr[i]; +}; + + +/** + * Search an array for the first element that satisfies a given condition and + * return its index. + * @param {IArrayLike|string} arr Array or array + * like object over which to iterate. + * @param {?function(this:S, T, number, ?) : boolean} f The function to call for + * every element. This function + * takes 3 arguments (the element, the index and the array) and should + * return a boolean. + * @param {S=} opt_obj An optional "this" context for the function. + * @return {number} The index of the first array element that passes the test, + * or -1 if no element is found. + * @template T,S + */ +goog.array.findIndex = function(arr, f, opt_obj) { + var l = arr.length; // must be fixed during loop... see docs + var arr2 = goog.isString(arr) ? arr.split('') : arr; + for (var i = 0; i < l; i++) { + if (i in arr2 && f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) { + return i; + } + } + return -1; +}; + + +/** + * Search an array (in reverse order) for the last element that satisfies a + * given condition and return that element. + * @param {IArrayLike|string} 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|null} The last array element that passes the test, or null if no + * element is found. + * @template T,S + */ +goog.array.findRight = function(arr, f, opt_obj) { + var i = goog.array.findIndexRight(arr, f, opt_obj); + return i < 0 ? null : goog.isString(arr) ? arr.charAt(i) : arr[i]; +}; + + +/** + * Search an array (in reverse order) for the last element that satisfies a + * given condition and return its index. + * @param {IArrayLike|string} arr Array or array + * like object over which to iterate. + * @param {?function(this:S, T, number, ?) : boolean} f The function to call + * for every element. This function + * takes 3 arguments (the element, the index and the array) and should + * return a boolean. + * @param {S=} opt_obj An optional "this" context for the function. + * @return {number} The index of the last array element that passes the test, + * or -1 if no element is found. + * @template T,S + */ +goog.array.findIndexRight = function(arr, f, opt_obj) { + var l = arr.length; // must be fixed during loop... see docs + var arr2 = goog.isString(arr) ? arr.split('') : arr; + for (var i = l - 1; i >= 0; i--) { + if (i in arr2 && f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) { + return i; + } + } + return -1; +}; + + +/** + * Whether the array contains the given object. + * @param {IArrayLike|string} arr The array to test for the presence of the + * element. + * @param {*} obj The object for which to test. + * @return {boolean} true if obj is present. + */ +goog.array.contains = function(arr, obj) { + return goog.array.indexOf(arr, obj) >= 0; +}; + + +/** + * Whether the array is empty. + * @param {IArrayLike|string} arr The array to test. + * @return {boolean} true if empty. + */ +goog.array.isEmpty = function(arr) { + return arr.length == 0; +}; + + +/** + * Clears the array. + * @param {IArrayLike} arr Array or array like object to clear. + */ +goog.array.clear = function(arr) { + // For non real arrays we don't have the magic length so we delete the + // indices. + if (!goog.isArray(arr)) { + for (var i = arr.length - 1; i >= 0; i--) { + delete arr[i]; + } + } + arr.length = 0; +}; + + +/** + * 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 {T} obj Value to add. + * @template T + */ +goog.array.insert = function(arr, obj) { + if (!goog.array.contains(arr, obj)) { + arr.push(obj); + } +}; + + +/** + * Inserts an object at the given index of the array. + * @param {IArrayLike} arr The array to modify. + * @param {*} obj The object to insert. + * @param {number=} opt_i The index at which to insert the object. If omitted, + * treated as 0. A negative index is counted from the end of the array. + */ +goog.array.insertAt = function(arr, obj, opt_i) { + goog.array.splice(arr, opt_i, 0, obj); +}; + + +/** + * Inserts at the given index of the array, all elements of another array. + * @param {IArrayLike} arr The array to modify. + * @param {IArrayLike} elementsToAdd The array of elements to add. + * @param {number=} opt_i The index at which to insert the object. If omitted, + * treated as 0. A negative index is counted from the end of the array. + */ +goog.array.insertArrayAt = function(arr, elementsToAdd, opt_i) { + goog.partial(goog.array.splice, arr, opt_i, 0).apply(null, elementsToAdd); +}; + + +/** + * Inserts an object into an array before a specified object. + * @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. + * @template T + */ +goog.array.insertBefore = function(arr, obj, opt_obj2) { + var i; + if (arguments.length == 2 || (i = goog.array.indexOf(arr, opt_obj2)) < 0) { + arr.push(obj); + } else { + goog.array.insertAt(arr, obj, i); + } +}; + + +/** + * Removes the first occurrence of a particular value from an array. + * @param {IArrayLike} arr Array from which to remove + * value. + * @param {T} obj Object to remove. + * @return {boolean} True if an element was removed. + * @template T + */ +goog.array.remove = function(arr, obj) { + var i = goog.array.indexOf(arr, obj); + var rv; + if ((rv = i >= 0)) { + goog.array.removeAt(arr, i); + } + return rv; +}; + + +/** + * Removes the last occurrence of a particular value from an array. + * @param {!IArrayLike} arr Array from which to remove value. + * @param {T} obj Object to remove. + * @return {boolean} True if an element was removed. + * @template T + */ +goog.array.removeLast = function(arr, obj) { + var i = goog.array.lastIndexOf(arr, obj); + if (i >= 0) { + goog.array.removeAt(arr, i); + return true; + } + return false; +}; + + +/** + * Removes from an array the element at index i + * @param {IArrayLike} arr Array or array like object from which to + * remove value. + * @param {number} i The index to remove. + * @return {boolean} True if an element was removed. + */ +goog.array.removeAt = function(arr, i) { + goog.asserts.assert(arr.length != null); + + // use generic form of splice + // splice returns the removed items and if successful the length of that + // will be 1 + return Array.prototype.splice.call(arr, i, 1).length == 1; +}; + + +/** + * Removes the first value that satisfies the given condition. + * @param {IArrayLike} 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 {boolean} True if an element was removed. + * @template T,S + */ +goog.array.removeIf = function(arr, f, opt_obj) { + var i = goog.array.findIndex(arr, f, opt_obj); + if (i >= 0) { + goog.array.removeAt(arr, i); + return true; + } + return false; +}; + + +/** + * Removes all values that satisfy the given condition. + * @param {IArrayLike} arr Array or array + * like object over which to iterate. + * @param {?function(this:S, T, number, ?) : boolean} f The function to call + * for every element. This function + * takes 3 arguments (the element, the index and the array) and should + * return a boolean. + * @param {S=} opt_obj An optional "this" context for the function. + * @return {number} The number of items removed + * @template T,S + */ +goog.array.removeAllIf = function(arr, f, opt_obj) { + var removedCount = 0; + goog.array.forEachRight(arr, function(val, index) { + if (f.call(/** @type {?} */ (opt_obj), val, index, arr)) { + if (goog.array.removeAt(arr, index)) { + removedCount++; + } + } + }); + return removedCount; +}; + + +/** + * Returns a new array that is the result of joining the arguments. If arrays + * are passed then their items are added, however, if non-arrays are passed they + * will be added to the return array as is. + * + * Note that ArrayLike objects will be added as is, rather than having their + * items added. + * + * goog.array.concat([1, 2], [3, 4]) -> [1, 2, 3, 4] + * goog.array.concat(0, [1, 2]) -> [0, 1, 2] + * goog.array.concat([1, 2], null) -> [1, 2, null] + * + * There is bug in all current versions of IE (6, 7 and 8) where arrays created + * in an iframe become corrupted soon (not immediately) after the iframe is + * destroyed. This is common if loading data via goog.net.IframeIo, for example. + * This corruption only affects the concat method which will start throwing + * Catastrophic Errors (#-2147418113). + * + * See http://endoflow.com/scratch/corrupted-arrays.html for a test case. + * + * Internally goog.array should use this, so that all methods will continue to + * work on these broken array objects. + * + * @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. + */ +goog.array.concat = function(var_args) { + return Array.prototype.concat.apply([], arguments); +}; + + +/** + * Returns a new array that contains the contents of all the arrays passed. + * @param {...!Array} var_args + * @return {!Array} + * @template T + */ +goog.array.join = function(var_args) { + return Array.prototype.concat.apply([], arguments); +}; + + +/** + * Converts an object to an array. + * @param {IArrayLike|string} object The object to convert to an + * array. + * @return {!Array} The object converted into an array. If object has a + * length property, every property indexed with a non-negative number + * less than length will be included in the result. If object does not + * have a length property, an empty array will be returned. + * @template T + */ +goog.array.toArray = function(object) { + var length = object.length; + + // If length is not a number the following is false. This case is kept for + // backwards compatibility since there are callers that pass objects that are + // not array like. + if (length > 0) { + var rv = new Array(length); + for (var i = 0; i < length; i++) { + rv[i] = object[i]; + } + return rv; + } + return []; +}; + + +/** + * Does a shallow copy of an array. + * @param {IArrayLike|string} arr Array or array-like object to + * clone. + * @return {!Array} Clone of the input array. + * @template T + */ +goog.array.clone = goog.array.toArray; + + +/** + * Extends an array with another array, element, or "array like" object. + * This function operates 'in-place', it does not create a new Array. + * + * Example: + * var a = []; + * goog.array.extend(a, [0, 1]); + * a; // [0, 1] + * goog.array.extend(a, 2); + * a; // [0, 1, 2] + * + * @param {Array} arr1 The array to modify. + * @param {...(IArrayLike|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 (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]; + } + } else { + arr1.push(arr2); + } + } +}; + + +/** + * Adds or removes elements from an array. This is a generic version of Array + * splice. This means that it might work on other objects similar to arrays, + * such as the arguments object. + * + * @param {IArrayLike} arr The array to modify. + * @param {number|undefined} index The index at which to start changing the + * array. If not defined, treated as 0. + * @param {number} howMany How many elements to remove (0 means no removal. A + * value below 0 is treated as zero and so is any other non number. Numbers + * are floored). + * @param {...T} var_args Optional, additional elements to insert into the + * array. + * @return {!Array} the removed elements. + * @template T + */ +goog.array.splice = function(arr, index, howMany, var_args) { + goog.asserts.assert(arr.length != null); + + return Array.prototype.splice.apply(arr, goog.array.slice(arguments, 1)); +}; + + +/** + * Returns a new array from a segment of an array. This is a generic version of + * Array slice. This means that it might work on other objects similar to + * arrays, such as the arguments object. + * + * @param {IArrayLike|string} 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 + * original array. + * @template T + */ +goog.array.slice = function(arr, start, opt_end) { + goog.asserts.assert(arr.length != null); + + // passing 1 arg to slice is not the same as passing 2 where the second is + // null or undefined (in that case the second argument is treated as 0). + // we could use slice on the arguments object and then use apply instead of + // testing the length + if (arguments.length <= 2) { + return Array.prototype.slice.call(arr, start); + } else { + return Array.prototype.slice.call(arr, start, opt_end); + } +}; + + +/** + * Removes all duplicates from an array (retaining only the first + * occurrence of each array element). This function modifies the + * array in place and doesn't change the order of the non-duplicate items. + * + * For objects, duplicates are identified as having the same unique ID as + * defined by {@link goog.getUid}. + * + * Alternatively you can specify a custom hash function that returns a unique + * value for each item in the array it should consider unique. + * + * Runtime: N, + * Worstcase space: 2N (no dupes) + * + * @param {IArrayLike} arr The array from which to remove + * duplicates. + * @param {Array=} opt_rv An optional array in which to return the results, + * instead of performing the removal inplace. If specified, the original + * array will remain unchanged. + * @param {function(T):string=} opt_hashFn An optional function to use to + * apply to every item in the array. This function should return a unique + * value for each item in the array it should consider unique. + * @template T + */ +goog.array.removeDuplicates = function(arr, opt_rv, opt_hashFn) { + var returnArray = opt_rv || arr; + var defaultHashFn = function(item) { + // Prefix each type with a single character representing the type to + // prevent conflicting keys (e.g. true and 'true'). + return goog.isObject(item) ? 'o' + goog.getUid(item) : + (typeof item).charAt(0) + item; + }; + var hashFn = opt_hashFn || defaultHashFn; + + var seen = {}, cursorInsert = 0, cursorRead = 0; + while (cursorRead < arr.length) { + var current = arr[cursorRead++]; + var key = hashFn(current); + if (!Object.prototype.hasOwnProperty.call(seen, key)) { + seen[key] = true; + returnArray[cursorInsert++] = current; + } + } + returnArray.length = cursorInsert; +}; + + +/** + * Searches the specified array for the specified target using the binary + * search algorithm. If no opt_compareFn is specified, elements are compared + * using goog.array.defaultCompare, which compares the elements + * using the built in < and > operators. This will produce the expected + * behavior for homogeneous arrays of String(s) and Number(s). The array + * specified must be sorted in ascending order (as defined by the + * comparison function). If the array is not sorted, results are undefined. + * If the array contains multiple instances of the specified target value, any + * of these instances may be found. + * + * Runtime: O(log n) + * + * @param {IArrayLike} arr The array to be searched. + * @param {TARGET} target The sought value. + * @param {function(TARGET, VALUE): number=} opt_compareFn Optional comparison + * function by which the array is ordered. Should take 2 arguments to + * compare, the target value and an element from your array, and return a + * negative number, zero, or a positive number depending on whether the + * first argument is less than, equal to, or greater than the second. + * @return {number} Lowest index of the target value if found, otherwise + * (-(insertion point) - 1). The insertion point is where the value should + * be inserted into arr to preserve the sorted property. Return value >= 0 + * iff target is found. + * @template TARGET, VALUE + */ +goog.array.binarySearch = function(arr, target, opt_compareFn) { + return goog.array.binarySearch_( + arr, opt_compareFn || goog.array.defaultCompare, false /* isEvaluator */, + target); +}; + + +/** + * Selects an index in the specified array using the binary search algorithm. + * The evaluator receives an element and determines whether the desired index + * is before, at, or after it. The evaluator must be consistent (formally, + * goog.array.map(goog.array.map(arr, evaluator, opt_obj), goog.math.sign) + * must be monotonically non-increasing). + * + * Runtime: O(log n) + * + * @param {IArrayLike} arr The array to be searched. + * @param {function(this:THIS, VALUE, number, ?): number} evaluator + * Evaluator function that receives 3 arguments (the element, the index and + * the array). Should return a negative number, zero, or a positive number + * depending on whether the desired index is before, at, or after the + * element passed to it. + * @param {THIS=} opt_obj The object to be used as the value of 'this' + * within evaluator. + * @return {number} Index of the leftmost element matched by the evaluator, if + * such exists; otherwise (-(insertion point) - 1). The insertion point is + * the index of the first element for which the evaluator returns negative, + * or arr.length if no such element exists. The return value is non-negative + * iff a match is found. + * @template THIS, VALUE + */ +goog.array.binarySelect = function(arr, evaluator, opt_obj) { + return goog.array.binarySearch_( + arr, evaluator, true /* isEvaluator */, undefined /* opt_target */, + opt_obj); +}; + + +/** + * Implementation of a binary search algorithm which knows how to use both + * comparison functions and evaluators. If an evaluator is provided, will call + * the evaluator with the given optional data object, conforming to the + * interface defined in binarySelect. Otherwise, if a comparison function is + * provided, will call the comparison function against the given data object. + * + * This implementation purposefully does not use goog.bind or goog.partial for + * performance reasons. + * + * Runtime: O(log n) + * + * @param {IArrayLike} arr The array to be searched. + * @param {function(?, ?, ?): number | function(?, ?): number} compareFn + * Either an evaluator or a comparison function, as defined by binarySearch + * and binarySelect above. + * @param {boolean} isEvaluator Whether the function is an evaluator or a + * comparison function. + * @param {?=} opt_target If the function is a comparison function, then + * this is the target to binary search for. + * @param {Object=} opt_selfObj If the function is an evaluator, this is an + * optional this object for the evaluator. + * @return {number} Lowest index of the target value if found, otherwise + * (-(insertion point) - 1). The insertion point is where the value should + * be inserted into arr to preserve the sorted property. Return value >= 0 + * iff target is found. + * @private + */ +goog.array.binarySearch_ = function( + arr, compareFn, isEvaluator, opt_target, opt_selfObj) { + var left = 0; // inclusive + var right = arr.length; // exclusive + var found; + while (left < right) { + var middle = (left + right) >> 1; + var compareResult; + if (isEvaluator) { + compareResult = compareFn.call(opt_selfObj, arr[middle], middle, arr); + } else { + // NOTE(dimvar): To avoid this cast, we'd have to use function overloading + // for the type of binarySearch_, which the type system can't express yet. + compareResult = /** @type {function(?, ?): number} */ (compareFn)( + opt_target, arr[middle]); + } + if (compareResult > 0) { + left = middle + 1; + } else { + right = middle; + // We are looking for the lowest index so we can't return immediately. + found = !compareResult; + } + } + // left is the index if found, or the insertion point otherwise. + // ~left is a shorthand for -left - 1. + return found ? left : ~left; +}; + + +/** + * Sorts the specified array into ascending order. If no opt_compareFn is + * specified, elements are compared using + * goog.array.defaultCompare, which compares the elements using + * the built in < and > operators. This will produce the expected behavior + * for homogeneous arrays of String(s) and Number(s), unlike the native sort, + * but will give unpredictable results for heterogeneous lists of strings and + * numbers with different numbers of digits. + * + * This sort is not guaranteed to be stable. + * + * Runtime: Same as Array.prototype.sort + * + * @param {Array} 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 whether the + * first argument is less than, equal to, or greater than the second. + * @template T + */ +goog.array.sort = function(arr, opt_compareFn) { + // TODO(arv): Update type annotation since null is not accepted. + arr.sort(opt_compareFn || goog.array.defaultCompare); +}; + + +/** + * Sorts the specified array into ascending order in a stable way. If no + * opt_compareFn is specified, elements are compared using + * goog.array.defaultCompare, which compares the elements using + * the built in < and > operators. This will produce the expected behavior + * for homogeneous arrays of String(s) and Number(s). + * + * Runtime: Same as Array.prototype.sort, plus an additional + * O(n) overhead of copying the array twice. + * + * @param {Array} 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 + * whether the first argument is less than, equal to, or greater than the + * second. + * @template T + */ +goog.array.stableSort = function(arr, opt_compareFn) { + var compArr = new Array(arr.length); + for (var i = 0; i < arr.length; i++) { + compArr[i] = {index: i, value: arr[i]}; + } + var valueCompareFn = opt_compareFn || goog.array.defaultCompare; + function stableCompareFn(obj1, obj2) { + return valueCompareFn(obj1.value, obj2.value) || obj1.index - obj2.index; + } + goog.array.sort(compArr, stableCompareFn); + for (var i = 0; i < arr.length; i++) { + arr[i] = compArr[i].value; + } +}; + + +/** + * Sort the specified array into ascending order based on item keys + * returned by the specified key function. + * If no opt_compareFn is specified, the keys are compared in ascending order + * using goog.array.defaultCompare. + * + * Runtime: O(S(f(n)), where S is runtime of goog.array.sort + * and f(n) is runtime of the key function. + * + * @param {Array} arr The array to be sorted. + * @param {function(T): K} keyFn Function taking array element and returning + * a key used for sorting this element. + * @param {?function(K, K): number=} opt_compareFn Optional comparison function + * by which the keys are to be ordered. Should take 2 arguments to compare, + * and return a negative number, zero, or a positive number depending on + * whether the first argument is less than, equal to, or greater than the + * second. + * @template T,K + */ +goog.array.sortByKey = function(arr, keyFn, opt_compareFn) { + var keyCompareFn = opt_compareFn || goog.array.defaultCompare; + goog.array.sort( + arr, function(a, b) { return keyCompareFn(keyFn(a), keyFn(b)); }); +}; + + +/** + * Sorts an array of objects by the specified object key and compare + * function. If no compare function is provided, the key values are + * compared in ascending order using goog.array.defaultCompare. + * This won't work for keys that get renamed by the compiler. So use + * {'foo': 1, 'bar': 2} rather than {foo: 1, bar: 2}. + * @param {Array} arr An array of objects to sort. + * @param {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) { + goog.array.sortByKey(arr, function(obj) { return obj[key]; }, opt_compareFn); +}; + + +/** + * Tells if the array is sorted. + * @param {!IArrayLike} 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, + * or a positive number depending on whether the first argument is less + * than, equal to, or greater than the second. + * @param {boolean=} opt_strict If true no equal elements are allowed. + * @return {boolean} Whether the array is sorted. + * @template T + */ +goog.array.isSorted = function(arr, opt_compareFn, opt_strict) { + var compare = opt_compareFn || goog.array.defaultCompare; + for (var i = 1; i < arr.length; i++) { + var compareResult = compare(arr[i - 1], arr[i]); + if (compareResult > 0 || compareResult == 0 && opt_strict) { + return false; + } + } + return true; +}; + + +/** + * Compares two arrays for equality. Two arrays are considered equal if they + * have the same length and their corresponding elements are equal according to + * the comparison function. + * + * @param {IArrayLike} arr1 The first array to compare. + * @param {IArrayLike} arr2 The second array to compare. + * @param {Function=} opt_equalsFn Optional comparison function. + * Should take 2 arguments to compare, and return true if the arguments + * are equal. Defaults to {@link goog.array.defaultCompareEquality} which + * compares the elements using the built-in '===' operator. + * @return {boolean} Whether the two arrays are equal. + */ +goog.array.equals = function(arr1, arr2, opt_equalsFn) { + if (!goog.isArrayLike(arr1) || !goog.isArrayLike(arr2) || + arr1.length != arr2.length) { + return false; + } + var l = arr1.length; + var equalsFn = opt_equalsFn || goog.array.defaultCompareEquality; + for (var i = 0; i < l; i++) { + if (!equalsFn(arr1[i], arr2[i])) { + return false; + } + } + return true; +}; + + +/** + * 3-way array compare function. + * @param {!IArrayLike} arr1 The first array to + * compare. + * @param {!IArrayLike} arr2 The second array to + * compare. + * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison + * function by which the array is to be ordered. Should take 2 arguments to + * compare, and return a negative number, zero, or a positive number + * depending on whether the first argument is less than, equal to, or + * greater than the second. + * @return {number} Negative number, zero, or a positive number depending on + * whether the first argument is less than, equal to, or greater than the + * second. + * @template VALUE + */ +goog.array.compare3 = function(arr1, arr2, opt_compareFn) { + var compare = opt_compareFn || goog.array.defaultCompare; + var l = Math.min(arr1.length, arr2.length); + for (var i = 0; i < l; i++) { + var result = compare(arr1[i], arr2[i]); + if (result != 0) { + return result; + } + } + return goog.array.defaultCompare(arr1.length, arr2.length); +}; + + +/** + * Compares its two arguments for 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 less than, equal to, or greater than the second, + * respectively. + * @template VALUE + */ +goog.array.defaultCompare = function(a, b) { + return a > b ? 1 : a < b ? -1 : 0; +}; + + +/** + * Compares its two arguments for inverse order, using the built in < and > + * operators. + * @param {VALUE} a The first object to be compared. + * @param {VALUE} b The second object to be compared. + * @return {number} A negative number, zero, or a positive number as the first + * argument is greater than, equal to, or less than the second, + * respectively. + * @template VALUE + */ +goog.array.inverseDefaultCompare = function(a, b) { + return -goog.array.defaultCompare(a, b); +}; + + +/** + * Compares its two arguments for equality, using the built in === operator. + * @param {*} a The first object to compare. + * @param {*} b The second object to compare. + * @return {boolean} True if the two arguments are equal, false otherwise. + */ +goog.array.defaultCompareEquality = function(a, b) { + return a === b; +}; + + +/** + * Inserts a value into a sorted array. The array is not modified if the + * value is already present. + * @param {IArrayLike} array The array to modify. + * @param {VALUE} value The object to insert. + * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison + * function by which the array is ordered. Should take 2 arguments to + * compare, and return a negative number, zero, or a positive number + * depending on whether the first argument is less than, equal to, or + * greater than the second. + * @return {boolean} True if an element was inserted. + * @template VALUE + */ +goog.array.binaryInsert = function(array, value, opt_compareFn) { + var index = goog.array.binarySearch(array, value, opt_compareFn); + if (index < 0) { + goog.array.insertAt(array, value, -(index + 1)); + return true; + } + return false; +}; + + +/** + * Removes a value from a sorted array. + * @param {!IArrayLike} array The array to modify. + * @param {VALUE} value The object to remove. + * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison + * function by which the array is ordered. Should take 2 arguments to + * compare, and return a negative number, zero, or a positive number + * depending on whether the first argument is less than, equal to, or + * greater than the second. + * @return {boolean} True if an element was removed. + * @template VALUE + */ +goog.array.binaryRemove = function(array, value, opt_compareFn) { + var index = goog.array.binarySearch(array, value, opt_compareFn); + return (index >= 0) ? goog.array.removeAt(array, index) : false; +}; + + +/** + * Splits an array into disjoint buckets according to a splitting function. + * @param {IArrayLike} array The array. + * @param {function(this:S, T, number, !IArrayLike):?} 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. + * @param {S=} opt_obj The object to be used as the value of 'this' within + * sorter. + * @return {!Object>} An object, with keys being all of the unique + * return values of sorter, and values being arrays containing the items for + * which the splitter returned that key. + * @template T,S + */ +goog.array.bucket = function(array, sorter, opt_obj) { + var buckets = {}; + + for (var i = 0; i < array.length; i++) { + var value = array[i]; + var key = sorter.call(/** @type {?} */ (opt_obj), value, i, array); + if (goog.isDef(key)) { + // Push the value to the right bucket, creating it if necessary. + var bucket = buckets[key] || (buckets[key] = []); + bucket.push(value); + } + } + + return buckets; +}; + + +/** + * Creates a new object built from the provided array and the key-generation + * function. + * @param {IArrayLike} 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 + * index and the array) and should return a string that will be used as the + * key for the element in the new object. If the function returns the same + * key for more than one element, the value for that key is + * implementation-defined. + * @param {S=} opt_obj The object to be used as the value of 'this' + * within keyFunc. + * @return {!Object} The new object. + * @template T,S + */ +goog.array.toObject = function(arr, keyFunc, opt_obj) { + var ret = {}; + goog.array.forEach(arr, function(element, index) { + ret[keyFunc.call(/** @type {?} */ (opt_obj), element, index, arr)] = + element; + }); + return ret; +}; + + +/** + * Creates a range of numbers in an arithmetic progression. + * + * Range takes 1, 2, or 3 arguments: + *
+ * range(5) is the same as range(0, 5, 1) and produces [0, 1, 2, 3, 4]
+ * range(2, 5) is the same as range(2, 5, 1) and produces [2, 3, 4]
+ * range(-2, -5, -1) produces [-2, -3, -4]
+ * range(-2, -5, 1) produces [], since stepping by 1 wouldn't ever reach -5.
+ * 
+ * + * @param {number} startOrEnd The starting value of the range if an end argument + * is provided. Otherwise, the start value is 0, and this is the end value. + * @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 + * an empty array if adding the step would not converge toward the end + * value. + */ +goog.array.range = function(startOrEnd, opt_end, opt_step) { + var array = []; + var start = 0; + var end = startOrEnd; + var step = opt_step || 1; + if (opt_end !== undefined) { + start = startOrEnd; + end = opt_end; + } + + if (step * (end - start) < 0) { + // Sign mismatch: start + step will never reach the end value. + return []; + } + + if (step > 0) { + for (var i = start; i < end; i += step) { + array.push(i); + } + } else { + for (var i = start; i > end; i += step) { + array.push(i); + } + } + return array; +}; + + +/** + * Returns an array consisting of the given value repeated N times. + * + * @param {VALUE} value The value to repeat. + * @param {number} n The repeat count. + * @return {!Array} An array with the repeated value. + * @template VALUE + */ +goog.array.repeat = function(value, n) { + var array = []; + for (var i = 0; i < n; i++) { + array[i] = value; + } + return array; +}; + + +/** + * Returns an array consisting of every argument with all arrays + * expanded in-place recursively. + * + * @param {...*} var_args The values to flatten. + * @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)) { + 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); + } + } + return result; +}; + + +/** + * Rotates an array in-place. After calling this method, the element at + * index i will be the element previously at index (i - n) % + * array.length, for all values of i between 0 and array.length - 1, + * inclusive. + * + * For example, suppose list comprises [t, a, n, k, s]. After invoking + * rotate(array, 1) (or rotate(array, -4)), array will comprise [s, t, a, n, k]. + * + * @param {!Array} array The array to rotate. + * @param {number} n The amount to rotate. + * @return {!Array} The array. + * @template T + */ +goog.array.rotate = function(array, n) { + goog.asserts.assert(array.length != null); + + if (array.length) { + n %= array.length; + if (n > 0) { + Array.prototype.unshift.apply(array, array.splice(-n, n)); + } else if (n < 0) { + Array.prototype.push.apply(array, array.splice(0, -n)); + } + } + return array; +}; + + +/** + * Moves one item of an array to a new position keeping the order of the rest + * of the items. Example use case: keeping a list of JavaScript objects + * synchronized with the corresponding list of DOM elements after one of the + * elements has been dragged to a new position. + * @param {!IArrayLike} arr The array to modify. + * @param {number} fromIndex Index of the item to move between 0 and + * {@code arr.length - 1}. + * @param {number} toIndex Target index between 0 and {@code arr.length - 1}. + */ +goog.array.moveItem = function(arr, fromIndex, toIndex) { + goog.asserts.assert(fromIndex >= 0 && fromIndex < arr.length); + goog.asserts.assert(toIndex >= 0 && toIndex < arr.length); + // Remove 1 item at fromIndex. + var removedItems = Array.prototype.splice.call(arr, fromIndex, 1); + // Insert the removed item at toIndex. + Array.prototype.splice.call(arr, toIndex, 0, removedItems[0]); + // We don't use goog.array.insertAt and goog.array.removeAt, because they're + // significantly slower than splice. +}; + + +/** + * Creates a new array for which the element at position i is an array of the + * ith element of the provided arrays. The returned array will only be as long + * as the shortest array provided; additional values are ignored. For example, + * the result of zipping [1, 2] and [3, 4, 5] is [[1,3], [2, 4]]. + * + * This is similar to the zip() function in Python. See {@link + * http://docs.python.org/library/functions.html#zip} + * + * @param {...!IArrayLike} var_args Arrays to be combined. + * @return {!Array>} A new array of arrays created from + * provided arrays. + */ +goog.array.zip = function(var_args) { + if (!arguments.length) { + return []; + } + var result = []; + var minLen = arguments[0].length; + for (var i = 1; i < arguments.length; i++) { + if (arguments[i].length < minLen) { + minLen = arguments[i].length; + } + } + for (var i = 0; i < minLen; i++) { + var value = []; + for (var j = 0; j < arguments.length; j++) { + value.push(arguments[j][i]); + } + result.push(value); + } + return result; +}; + + +/** + * Shuffles the values in the specified array using the Fisher-Yates in-place + * shuffle (also known as the Knuth Shuffle). By default, calls Math.random() + * and so resets the state of that random number generator. Similarly, may reset + * the state of the any other specified random number generator. + * + * Runtime: O(n) + * + * @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). + * Defaults to Math.random() using JavaScript's built-in Math library. + */ +goog.array.shuffle = function(arr, opt_randFn) { + var randFn = opt_randFn || Math.random; + + for (var i = arr.length - 1; i > 0; i--) { + // Choose a random array index in [0, i] (inclusive with i). + var j = Math.floor(randFn() * (i + 1)); + + var tmp = arr[i]; + arr[i] = arr[j]; + 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 {!IArrayLike} arr The array to get a indexed copy from. + * @param {!IArrayLike} 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; +}; + + +/** + * Maps each element of the input array into zero or more elements of the output + * array. + * + * @param {!IArrayLike|string} arr Array or array like object + * over which to iterate. + * @param {function(this:THIS, VALUE, number, ?): !Array} f The function + * to call for every element. This function takes 3 arguments (the element, + * the index and the array) and should return an array. The result will be + * used to extend 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 concatenation of all arrays + * returned from f. + * @template THIS, VALUE, RESULT + */ +goog.array.concatMap = function(arr, f, opt_obj) { + return goog.array.concat.apply([], goog.array.map(arr, f, opt_obj)); +}; diff --git a/closure-library/closure/goog/asserts/asserts.js b/closure-library/closure/goog/asserts/asserts.js new file mode 100644 index 0000000000..66578fd5df --- /dev/null +++ b/closure-library/closure/goog/asserts/asserts.js @@ -0,0 +1,422 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities to check the preconditions, postconditions and + * invariants runtime. + * + * Methods in this package are given special treatment by the compiler + * for type-inference. For example, goog.asserts.assert(foo) + * will make the compiler treat foo as non-nullable. Similarly, + * goog.asserts.assertNumber(foo) informs the compiler about the + * type of foo. Where applicable, such assertions are preferable to + * casts by jsdoc with @type. + * + * The compiler has an option to disable asserts. So code like: + * + * var x = goog.asserts.assert(foo()); + * goog.asserts.assert(bar()); + * + * will be transformed into: + * + * var x = foo(); + * + * The compiler will leave in foo() (because its return value is used), + * but it will remove bar() because it assumes it does not have side-effects. + * + * Additionally, note the compiler will consider the type to be "tightened" for + * all statements after the assertion. For example: + * + * const /** ?Object &#ast;/ value = foo(); + * goog.asserts.assert(value); + * // "value" is of type {!Object} at this point. + * + * + * @author agrieve@google.com (Andrew Grieve) + */ + +goog.provide('goog.asserts'); +goog.provide('goog.asserts.AssertionError'); + +goog.require('goog.debug.Error'); +goog.require('goog.dom.NodeType'); + + +/** + * @define {boolean} Whether to strip out asserts or to leave them in. + */ +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. + * @constructor + * @extends {goog.debug.Error} + * @final + */ +goog.asserts.AssertionError = function(messagePattern, messageArgs) { + goog.debug.Error.call(this, goog.asserts.subs_(messagePattern, messageArgs)); + + /** + * The message pattern used to format the error message. Error handlers can + * use this to uniquely identify the assertion. + * @type {string} + */ + this.messagePattern = messagePattern; +}; +goog.inherits(goog.asserts.AssertionError, goog.debug.Error); + + +/** @override */ +goog.asserts.AssertionError.prototype.name = 'AssertionError'; + + +/** + * The default error handler. + * @param {!goog.asserts.AssertionError} e The exception to be handled. + */ +goog.asserts.DEFAULT_ERROR_HANDLER = function(e) { + throw e; +}; + + +/** + * The handler responsible for throwing or logging assertion errors. + * @private {function(!goog.asserts.AssertionError)} + */ +goog.asserts.errorHandler_ = goog.asserts.DEFAULT_ERROR_HANDLER; + + +/** + * Does simple python-style string substitution. + * subs("foo%s hot%s", "bar", "dog") becomes "foobar hotdog". + * @param {string} pattern The string containing the pattern. + * @param {!Array<*>} subs The items to substitute into the pattern. + * @return {string} A copy of `str` in which each occurrence of + * {@code %s} has been replaced an argument from `var_args`. + * @private + */ +goog.asserts.subs_ = function(pattern, subs) { + var splitParts = pattern.split('%s'); + var returnString = ''; + + // Replace up to the last split part. We are inserting in the + // positions between split parts. + var subLast = splitParts.length - 1; + for (var i = 0; i < subLast; i++) { + // keep unsupplied as '%s' + var sub = (i < subs.length) ? subs[i] : '%s'; + returnString += splitParts[i] + sub; + } + return returnString + splitParts[subLast]; +}; + + +/** + * Throws an exception with the given message and "Assertion failed" prefixed + * onto it. + * @param {string} defaultMessage The message to use if givenMessage is empty. + * @param {Array<*>} defaultArgs The substitution arguments for defaultMessage. + * @param {string|undefined} givenMessage Message supplied by the caller. + * @param {Array<*>} givenArgs The substitution arguments for givenMessage. + * @throws {goog.asserts.AssertionError} When the value is not a number. + * @private + */ +goog.asserts.doAssertFailure_ = function( + defaultMessage, defaultArgs, givenMessage, givenArgs) { + var message = 'Assertion failed'; + if (givenMessage) { + message += ': ' + givenMessage; + var args = givenArgs; + } else if (defaultMessage) { + message += ': ' + defaultMessage; + args = defaultArgs; + } + // The '' + works around an Opera 10 bug in the unit tests. Without it, + // a stack trace is added to var message above. With this, a stack trace is + // not added until this line (it causes the extra garbage to be added after + // the assertion message instead of in the middle of it). + var e = new goog.asserts.AssertionError('' + message, args || []); + goog.asserts.errorHandler_(e); +}; + + +/** + * Sets a custom error handler that can be used to customize the behavior of + * assertion failures, for example by turning all assertion failures into log + * messages. + * @param {function(!goog.asserts.AssertionError)} errorHandler + */ +goog.asserts.setErrorHandler = function(errorHandler) { + if (goog.asserts.ENABLE_ASSERTS) { + goog.asserts.errorHandler_ = errorHandler; + } +}; + + +/** + * Checks if the condition evaluates to true if goog.asserts.ENABLE_ASSERTS is + * true. + * @template T + * @param {T} condition The condition to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @return {T} The value of the condition. + * @throws {goog.asserts.AssertionError} When the condition evaluates to false. + */ +goog.asserts.assert = function(condition, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && !condition) { + goog.asserts.doAssertFailure_( + '', null, opt_message, Array.prototype.slice.call(arguments, 2)); + } + return condition; +}; + + +/** + * Fails if goog.asserts.ENABLE_ASSERTS is true. This function is useful in case + * when we want to add a check in the unreachable area like switch-case + * statement: + * + *
+ *  switch(type) {
+ *    case FOO: doSomething(); break;
+ *    case BAR: doSomethingElse(); break;
+ *    default: goog.asserts.fail('Unrecognized type: ' + type);
+ *      // We have only 2 types - "default:" section is unreachable code.
+ *  }
+ * 
+ * + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @throws {goog.asserts.AssertionError} Failure. + */ +goog.asserts.fail = function(opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS) { + goog.asserts.errorHandler_( + new goog.asserts.AssertionError( + 'Failure' + (opt_message ? ': ' + opt_message : ''), + Array.prototype.slice.call(arguments, 1))); + } +}; + + +/** + * Checks if the value is a number if goog.asserts.ENABLE_ASSERTS is true. + * @param {*} value The value to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @return {number} The value, guaranteed to be a number when asserts enabled. + * @throws {goog.asserts.AssertionError} When the value is not a number. + */ +goog.asserts.assertNumber = function(value, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && !goog.isNumber(value)) { + goog.asserts.doAssertFailure_( + 'Expected number but got %s: %s.', [goog.typeOf(value), value], + opt_message, Array.prototype.slice.call(arguments, 2)); + } + return /** @type {number} */ (value); +}; + + +/** + * Checks if the value is a string if goog.asserts.ENABLE_ASSERTS is true. + * @param {*} value The value to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @return {string} The value, guaranteed to be a string when asserts enabled. + * @throws {goog.asserts.AssertionError} When the value is not a string. + */ +goog.asserts.assertString = function(value, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && !goog.isString(value)) { + goog.asserts.doAssertFailure_( + 'Expected string but got %s: %s.', [goog.typeOf(value), value], + opt_message, Array.prototype.slice.call(arguments, 2)); + } + return /** @type {string} */ (value); +}; + + +/** + * Checks if the value is a function if goog.asserts.ENABLE_ASSERTS is true. + * @param {*} value The value to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @return {!Function} The value, guaranteed to be a function when asserts + * enabled. + * @throws {goog.asserts.AssertionError} When the value is not a function. + */ +goog.asserts.assertFunction = function(value, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && !goog.isFunction(value)) { + goog.asserts.doAssertFailure_( + 'Expected function but got %s: %s.', [goog.typeOf(value), value], + opt_message, Array.prototype.slice.call(arguments, 2)); + } + return /** @type {!Function} */ (value); +}; + + +/** + * Checks if the value is an Object if goog.asserts.ENABLE_ASSERTS is true. + * @param {*} value The value to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @return {!Object} The value, guaranteed to be a non-null object. + * @throws {goog.asserts.AssertionError} When the value is not an object. + */ +goog.asserts.assertObject = function(value, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && !goog.isObject(value)) { + goog.asserts.doAssertFailure_( + 'Expected object but got %s: %s.', [goog.typeOf(value), value], + opt_message, Array.prototype.slice.call(arguments, 2)); + } + return /** @type {!Object} */ (value); +}; + + +/** + * Checks if the value is an Array if goog.asserts.ENABLE_ASSERTS is true. + * @param {*} value The value to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @return {!Array} The value, guaranteed to be a non-null array. + * @throws {goog.asserts.AssertionError} When the value is not an array. + */ +goog.asserts.assertArray = function(value, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && !goog.isArray(value)) { + goog.asserts.doAssertFailure_( + 'Expected array but got %s: %s.', [goog.typeOf(value), value], + opt_message, Array.prototype.slice.call(arguments, 2)); + } + return /** @type {!Array} */ (value); +}; + + +/** + * Checks if the value is a boolean if goog.asserts.ENABLE_ASSERTS is true. + * @param {*} value The value to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @return {boolean} The value, guaranteed to be a boolean when asserts are + * enabled. + * @throws {goog.asserts.AssertionError} When the value is not a boolean. + */ +goog.asserts.assertBoolean = function(value, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && !goog.isBoolean(value)) { + goog.asserts.doAssertFailure_( + 'Expected boolean but got %s: %s.', [goog.typeOf(value), value], + opt_message, Array.prototype.slice.call(arguments, 2)); + } + return /** @type {boolean} */ (value); +}; + + +/** + * Checks if the value is a DOM Element if goog.asserts.ENABLE_ASSERTS is true. + * @param {*} value The value to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @return {!Element} The value, likely to be a DOM Element when asserts are + * enabled. + * @throws {goog.asserts.AssertionError} When the value is not an Element. + */ +goog.asserts.assertElement = function(value, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && + (!goog.isObject(value) || value.nodeType != goog.dom.NodeType.ELEMENT)) { + goog.asserts.doAssertFailure_( + 'Expected Element but got %s: %s.', [goog.typeOf(value), value], + opt_message, Array.prototype.slice.call(arguments, 2)); + } + return /** @type {!Element} */ (value); +}; + + +/** + * Checks if the value is an instance of the user-defined type if + * goog.asserts.ENABLE_ASSERTS is true. + * + * The compiler may tighten the type returned by this function. + * + * @param {?} value The value to check. + * @param {function(new: T, ...)} type A user-defined constructor. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @throws {goog.asserts.AssertionError} When the value is not an instance of + * type. + * @return {T} + * @template T + */ +goog.asserts.assertInstanceof = function(value, type, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && !(value instanceof type)) { + goog.asserts.doAssertFailure_( + 'Expected instanceof %s but got %s.', + [goog.asserts.getType_(type), goog.asserts.getType_(value)], + opt_message, Array.prototype.slice.call(arguments, 3)); + } + return value; +}; + + +/** + * Checks whether the value is a finite number, if goog.asserts.ENABLE_ASSERTS + * is true. + * + * @param {*} value The value to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @throws {goog.asserts.AssertionError} When the value is not a number, or is + * a non-finite number such as NaN, Infinity or -Infinity. + * @return {number} The value initially passed in. + */ +goog.asserts.assertFinite = function(value, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && + (typeof value != 'number' || !isFinite(value))) { + goog.asserts.doAssertFailure_( + 'Expected %s to be a finite number but it is not.', [value], + opt_message, Array.prototype.slice.call(arguments, 2)); + } + return /** @type {number} */ (value); +}; + +/** + * Checks that no enumerable keys are present in Object.prototype. Such keys + * would break most code that use {@code for (var ... in ...)} loops. + */ +goog.asserts.assertObjectPrototypeIsIntact = function() { + for (var key in Object.prototype) { + goog.asserts.fail(key + ' should not be enumerable in Object.prototype.'); + } +}; + + +/** + * Returns the type of a value. If a constructor is passed, and a suitable + * string cannot be found, 'unknown type name' will be returned. + * @param {*} value A constructor, object, or primitive. + * @return {string} The best display name for the value, or 'unknown type name'. + * @private + */ +goog.asserts.getType_ = function(value) { + if (value instanceof Function) { + return value.displayName || value.name || 'unknown type name'; + } else if (value instanceof Object) { + return /** @type {string} */ (value.constructor.displayName) || + value.constructor.name || Object.prototype.toString.call(value); + } else { + return value === null ? 'null' : typeof value; + } +}; diff --git a/closure-library/closure/goog/async/animationdelay.js b/closure-library/closure/goog/async/animationdelay.js new file mode 100644 index 0000000000..7984370dcd --- /dev/null +++ b/closure-library/closure/goog/async/animationdelay.js @@ -0,0 +1,272 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A delayed callback that pegs to the next animation frame + * instead of a user-configurable timeout. + * + * @author nicksantos@google.com (Nick Santos) + */ + +goog.provide('goog.async.AnimationDelay'); + +goog.require('goog.Disposable'); +goog.require('goog.events'); +goog.require('goog.functions'); + + + +// TODO(nicksantos): Should we factor out the common code between this and +// goog.async.Delay? I'm not sure if there's enough code for this to really +// make sense. Subclassing seems like the wrong approach for a variety of +// reasons. Maybe there should be a common interface? + + + +/** + * A delayed callback that pegs to the next animation frame + * instead of a user configurable timeout. By design, this should have + * the same interface as goog.async.Delay. + * + * Uses requestAnimationFrame and friends when available, but falls + * back to a timeout of goog.async.AnimationDelay.TIMEOUT. + * + * For more on requestAnimationFrame and how you can use it to create smoother + * animations, see: + * @see http://paulirish.com/2011/requestanimationframe-for-smart-animating/ + * + * @param {function(this:THIS, number)} listener Function to call + * when the delay completes. Will be passed the timestamp when it's called, + * in unix ms. + * @param {Window=} opt_window The window object to execute the delay in. + * Defaults to the global object. + * @param {THIS=} opt_handler The object scope to invoke the function in. + * @template THIS + * @constructor + * @struct + * @extends {goog.Disposable} + * @final + */ +goog.async.AnimationDelay = function(listener, opt_window, opt_handler) { + goog.async.AnimationDelay.base(this, 'constructor'); + + /** + * Identifier of the active delay timeout, or event listener, + * or null when inactive. + * @private {?goog.events.Key|number} + */ + this.id_ = null; + + /** + * If we're using dom listeners. + * @private {?boolean} + */ + this.usingListeners_ = false; + + /** + * The function that will be invoked after a delay. + * @const + * @private + */ + this.listener_ = listener; + + /** + * The object context to invoke the callback in. + * @const + * @private {(THIS|undefined)} + */ + this.handler_ = opt_handler; + + /** + * @private {Window} + */ + this.win_ = opt_window || window; + + /** + * Cached callback function invoked when the delay finishes. + * @private {function()} + */ + this.callback_ = goog.bind(this.doAction_, this); +}; +goog.inherits(goog.async.AnimationDelay, goog.Disposable); + + +/** + * Default wait timeout for animations (in milliseconds). Only used for timed + * animation, which uses a timer (setTimeout) to schedule animation. + * + * @type {number} + * @const + */ +goog.async.AnimationDelay.TIMEOUT = 20; + + +/** + * Name of event received from the requestAnimationFrame in Firefox. + * + * @type {string} + * @const + * @private + */ +goog.async.AnimationDelay.MOZ_BEFORE_PAINT_EVENT_ = 'MozBeforePaint'; + + +/** + * Starts the delay timer. The provided listener function will be called + * before the next animation frame. + */ +goog.async.AnimationDelay.prototype.start = function() { + this.stop(); + this.usingListeners_ = false; + + var raf = this.getRaf_(); + var cancelRaf = this.getCancelRaf_(); + if (raf && !cancelRaf && this.win_.mozRequestAnimationFrame) { + // Because Firefox (Gecko) runs animation in separate threads, it also saves + // time by running the requestAnimationFrame callbacks in that same thread. + // Sadly this breaks the assumption of implicit thread-safety in JS, and can + // thus create thread-based inconsistencies on counters etc. + // + // Calling cycleAnimations_ using the MozBeforePaint event instead of as + // callback fixes this. + // + // Trigger this condition only if the mozRequestAnimationFrame is available, + // but not the W3C requestAnimationFrame function (as in draft) or the + // equivalent cancel functions. + this.id_ = goog.events.listen( + this.win_, goog.async.AnimationDelay.MOZ_BEFORE_PAINT_EVENT_, + this.callback_); + this.win_.mozRequestAnimationFrame(null); + this.usingListeners_ = true; + } else if (raf && cancelRaf) { + this.id_ = raf.call(this.win_, this.callback_); + } else { + this.id_ = this.win_.setTimeout( + // Prior to Firefox 13, Gecko passed a non-standard parameter + // to the callback that we want to ignore. + goog.functions.lock(this.callback_), goog.async.AnimationDelay.TIMEOUT); + } +}; + + +/** + * Starts the delay timer if it's not already active. + */ +goog.async.AnimationDelay.prototype.startIfNotActive = function() { + if (!this.isActive()) { + this.start(); + } +}; + + +/** + * Stops the delay timer if it is active. No action is taken if the timer is not + * in use. + */ +goog.async.AnimationDelay.prototype.stop = function() { + if (this.isActive()) { + var raf = this.getRaf_(); + var cancelRaf = this.getCancelRaf_(); + if (raf && !cancelRaf && this.win_.mozRequestAnimationFrame) { + goog.events.unlistenByKey(this.id_); + } else if (raf && cancelRaf) { + cancelRaf.call(this.win_, /** @type {number} */ (this.id_)); + } else { + this.win_.clearTimeout(/** @type {number} */ (this.id_)); + } + } + this.id_ = null; +}; + + +/** + * Fires delay's action even if timer has already gone off or has not been + * started yet; guarantees action firing. Stops the delay timer. + */ +goog.async.AnimationDelay.prototype.fire = function() { + this.stop(); + this.doAction_(); +}; + + +/** + * Fires delay's action only if timer is currently active. Stops the delay + * timer. + */ +goog.async.AnimationDelay.prototype.fireIfActive = function() { + if (this.isActive()) { + this.fire(); + } +}; + + +/** + * @return {boolean} True if the delay is currently active, false otherwise. + */ +goog.async.AnimationDelay.prototype.isActive = function() { + return this.id_ != null; +}; + + +/** + * Invokes the callback function after the delay successfully completes. + * @private + */ +goog.async.AnimationDelay.prototype.doAction_ = function() { + if (this.usingListeners_ && this.id_) { + goog.events.unlistenByKey(this.id_); + } + this.id_ = null; + + // We are not using the timestamp returned by requestAnimationFrame + // because it may be either a Date.now-style time or a + // high-resolution time (depending on browser implementation). Using + // goog.now() will ensure that the timestamp used is consistent and + // compatible with goog.fx.Animation. + this.listener_.call(this.handler_, goog.now()); +}; + + +/** @override */ +goog.async.AnimationDelay.prototype.disposeInternal = function() { + this.stop(); + goog.async.AnimationDelay.base(this, 'disposeInternal'); +}; + + +/** + * @return {?function(function(number)): number} The requestAnimationFrame + * function, or null if not available on this browser. + * @private + */ +goog.async.AnimationDelay.prototype.getRaf_ = function() { + var win = this.win_; + return win.requestAnimationFrame || win.webkitRequestAnimationFrame || + win.mozRequestAnimationFrame || win.oRequestAnimationFrame || + win.msRequestAnimationFrame || null; +}; + + +/** + * @return {?function(number): undefined} The cancelAnimationFrame function, + * or null if not available on this browser. + * @private + */ +goog.async.AnimationDelay.prototype.getCancelRaf_ = function() { + var win = this.win_; + return win.cancelAnimationFrame || win.cancelRequestAnimationFrame || + win.webkitCancelRequestAnimationFrame || + win.mozCancelRequestAnimationFrame || win.oCancelRequestAnimationFrame || + win.msCancelRequestAnimationFrame || null; +}; diff --git a/closure-library/closure/goog/async/conditionaldelay.js b/closure-library/closure/goog/async/conditionaldelay.js new file mode 100644 index 0000000000..2f45fee60f --- /dev/null +++ b/closure-library/closure/goog/async/conditionaldelay.js @@ -0,0 +1,227 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Defines a class useful for handling functions that must be + * invoked later when some condition holds. Examples include deferred function + * calls that return a boolean flag whether it succedeed or not. + * + * Example: + * + * function deferred() { + * var succeeded = false; + * // ... custom code + * return succeeded; + * } + * + * var deferredCall = new goog.async.ConditionalDelay(deferred); + * deferredCall.onSuccess = function() { + * alert('Success: The deferred function has been successfully executed.'); + * } + * deferredCall.onFailure = function() { + * alert('Failure: Time limit exceeded.'); + * } + * + * // Call the deferred() every 100 msec until it returns true, + * // or 5 seconds pass. + * deferredCall.start(100, 5000); + * + * // Stop the deferred function call (does nothing if it's not active). + * deferredCall.stop(); + * + */ + + +goog.provide('goog.async.ConditionalDelay'); + +goog.require('goog.Disposable'); +goog.require('goog.async.Delay'); + + + +/** + * A ConditionalDelay object invokes the associated function after a specified + * interval delay and checks its return value. If the function returns + * `true` the conditional delay is cancelled and {@see #onSuccess} + * is called. Otherwise this object keeps to invoke the deferred function until + * either it returns `true` or the timeout is exceeded. In the latter case + * the {@see #onFailure} method will be called. + * + * The interval duration and timeout can be specified each time the delay is + * started. Calling start on an active delay will reset the timer. + * + * @param {function():boolean} listener Function to call when the delay + * completes. Should return a value that type-converts to `true` if + * the call succeeded and this delay should be stopped. + * @param {Object=} opt_handler The object scope to invoke the function in. + * @constructor + * @struct + * @extends {goog.Disposable} + */ +goog.async.ConditionalDelay = function(listener, opt_handler) { + goog.async.ConditionalDelay.base(this, 'constructor'); + + /** + * The delay interval in milliseconds to between the calls to the callback. + * Note, that the callback may be invoked earlier than this interval if the + * timeout is exceeded. + * @private {number} + */ + this.interval_ = 0; + + /** + * The timeout timestamp until which the delay is to be executed. + * A negative value means no timeout. + * @private {number} + */ + this.runUntil_ = 0; + + /** + * True if the listener has been executed, and it returned `true`. + * @private {boolean} + */ + this.isDone_ = false; + + /** + * The function that will be invoked after a delay. + * @private {function():boolean} + */ + this.listener_ = listener; + + /** + * The object context to invoke the callback in. + * @private {Object|undefined} + */ + this.handler_ = opt_handler; + + /** + * The underlying goog.async.Delay delegate object. + * @private {goog.async.Delay} + */ + this.delay_ = new goog.async.Delay( + goog.bind(this.onTick_, this), 0 /*interval*/, this /*scope*/); +}; +goog.inherits(goog.async.ConditionalDelay, goog.Disposable); + + +/** @override */ +goog.async.ConditionalDelay.prototype.disposeInternal = function() { + this.delay_.dispose(); + delete this.listener_; + delete this.handler_; + goog.async.ConditionalDelay.superClass_.disposeInternal.call(this); +}; + + +/** + * Starts the delay timer. The provided listener function will be called + * repeatedly after the specified interval until the function returns + * `true` or the timeout is exceeded. Calling start on an active timer + * will stop the timer first. + * @param {number=} opt_interval The time interval between the function + * invocations (in milliseconds). Default is 0. + * @param {number=} opt_timeout The timeout interval (in milliseconds). Takes + * precedence over the `opt_interval`, i.e. if the timeout is less + * than the invocation interval, the function will be called when the + * timeout is exceeded. A negative value means no timeout. Default is 0. + */ +goog.async.ConditionalDelay.prototype.start = function( + opt_interval, opt_timeout) { + this.stop(); + this.isDone_ = false; + + var timeout = opt_timeout || 0; + this.interval_ = Math.max(opt_interval || 0, 0); + this.runUntil_ = timeout < 0 ? -1 : (goog.now() + timeout); + this.delay_.start( + timeout < 0 ? this.interval_ : Math.min(this.interval_, timeout)); +}; + + +/** + * Stops the delay timer if it is active. No action is taken if the timer is not + * in use. + */ +goog.async.ConditionalDelay.prototype.stop = function() { + this.delay_.stop(); +}; + + +/** + * @return {boolean} True if the delay is currently active, false otherwise. + */ +goog.async.ConditionalDelay.prototype.isActive = function() { + return this.delay_.isActive(); +}; + + +/** + * @return {boolean} True if the listener has been executed and returned + * `true` since the last call to {@see #start}. + */ +goog.async.ConditionalDelay.prototype.isDone = function() { + return this.isDone_; +}; + + +/** + * Called when the listener has been successfully executed and returned + * `true`. The {@see #isDone} method should return `true` by now. + * Designed for inheritance, should be overridden by subclasses or on the + * instances if they care. + */ +goog.async.ConditionalDelay.prototype.onSuccess = function() { + // Do nothing by default. +}; + + +/** + * Called when this delayed call is cancelled because the timeout has been + * exceeded, and the listener has never returned `true`. + * Designed for inheritance, should be overridden by subclasses or on the + * instances if they care. + */ +goog.async.ConditionalDelay.prototype.onFailure = function() { + // Do nothing by default. +}; + + +/** + * A callback function for the underlying `goog.async.Delay` object. When + * executed the listener function is called, and if it returns `true` + * the delay is stopped and the {@see #onSuccess} method is invoked. + * If the timeout is exceeded the delay is stopped and the + * {@see #onFailure} method is called. + * @private + */ +goog.async.ConditionalDelay.prototype.onTick_ = function() { + var successful = this.listener_.call(this.handler_); + if (successful) { + this.isDone_ = true; + this.onSuccess(); + } else { + // Try to reschedule the task. + if (this.runUntil_ < 0) { + // No timeout. + this.delay_.start(this.interval_); + } else { + var timeLeft = this.runUntil_ - goog.now(); + if (timeLeft <= 0) { + this.onFailure(); + } else { + this.delay_.start(Math.min(this.interval_, timeLeft)); + } + } + } +}; diff --git a/closure-library/closure/goog/async/debouncer.js b/closure-library/closure/goog/async/debouncer.js new file mode 100644 index 0000000000..848c09cead --- /dev/null +++ b/closure-library/closure/goog/async/debouncer.js @@ -0,0 +1,206 @@ +// 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 Definition of the goog.async.Debouncer class. + * + * @see ../demos/timers.html + */ + +goog.provide('goog.async.Debouncer'); + +goog.require('goog.Disposable'); +goog.require('goog.Timer'); + + + +/** + * Debouncer will perform a specified action exactly once for any sequence of + * signals fired repeatedly so long as they are fired less than a specified + * interval apart (in milliseconds). Whether it receives one signal or multiple, + * it will always wait until a full interval has elapsed since the last signal + * before performing the action. + * @param {function(this: T, ...?)} listener Function to callback when the + * action is triggered. + * @param {number} interval Interval over which to debounce. The listener will + * only be called after the full interval has elapsed since the last signal. + * @param {T=} opt_handler Object in whose scope to call the listener. + * @constructor + * @struct + * @extends {goog.Disposable} + * @final + * @template T + */ +goog.async.Debouncer = function(listener, interval, opt_handler) { + goog.async.Debouncer.base(this, 'constructor'); + + /** + * Function to callback + * @const @private {function(this: T, ...?)} + */ + this.listener_ = + opt_handler != null ? goog.bind(listener, opt_handler) : listener; + + /** + * Interval for the debounce time + * @const @private {number} + */ + this.interval_ = interval; + + /** + * Cached callback function invoked after the debounce timeout completes + * @const @private {!Function} + */ + this.callback_ = goog.bind(this.onTimer_, this); + + /** + * Indicates that the action is pending and needs to be fired. + * @private {boolean} + */ + this.shouldFire_ = false; + + /** + * Indicates the count of nested pauses currently in effect on the debouncer. + * When this count is not zero, fired actions will be postponed until the + * debouncer is resumed enough times to drop the pause count to zero. + * @private {number} + */ + this.pauseCount_ = 0; + + /** + * Timer for scheduling the next callback + * @private {?number} + */ + this.timer_ = null; + + /** + * When set this is a timestamp. On the onfire we want to reschedule the + * callback so it ends up at this time. + * @private {?number} + */ + this.refireAt_ = null; + + /** + * The last arguments passed into `fire`. + * @private {!IArrayLike} + */ + this.args_ = []; +}; +goog.inherits(goog.async.Debouncer, goog.Disposable); + + +/** + * Notifies the debouncer that the action has happened. It will debounce the + * call so that the callback is only called after the last action in a sequence + * of actions separated by periods less the interval parameter passed to the + * constructor, passing the arguments from the last call of this function into + * the debounced function. + * @param {...?} var_args Arguments to pass on to the debounced function. + */ +goog.async.Debouncer.prototype.fire = function(var_args) { + this.args_ = arguments; + // When this method is called, we need to prevent fire() calls from within the + // previous interval from calling the callback. The simplest way of doing this + // is to call this.stop() which calls clearTimeout, and then reschedule the + // timeout. However clearTimeout and setTimeout are expensive, so we just + // leave them untouched and when they do happen we potentially reschedule. + this.shouldFire_ = false; + if (this.timer_) { + this.refireAt_ = goog.now() + this.interval_; + return; + } + this.timer_ = goog.Timer.callOnce(this.callback_, this.interval_); +}; + + +/** + * Cancels any pending action callback. The debouncer can be restarted by + * calling {@link #fire}. + */ +goog.async.Debouncer.prototype.stop = function() { + if (this.timer_) { + goog.Timer.clear(this.timer_); + this.timer_ = null; + } + this.refireAt_ = null; + this.shouldFire_ = false; + this.args_ = []; +}; + + +/** + * Pauses the debouncer. All pending and future action callbacks will be delayed + * until the debouncer is resumed. Pauses can be nested. + */ +goog.async.Debouncer.prototype.pause = function() { + ++this.pauseCount_; +}; + + +/** + * Resumes the debouncer. If doing so drops the pausing count to zero, pending + * action callbacks will be executed as soon as possible, but still no sooner + * than an interval's delay after the previous call. Future action callbacks + * will be executed as normal. + */ +goog.async.Debouncer.prototype.resume = function() { + if (!this.pauseCount_) { + return; + } + + --this.pauseCount_; + if (!this.pauseCount_ && this.shouldFire_) { + this.doAction_(); + } +}; + + +/** @override */ +goog.async.Debouncer.prototype.disposeInternal = function() { + this.stop(); + goog.async.Debouncer.base(this, 'disposeInternal'); +}; + + +/** + * Handler for the timer to fire the debouncer. + * @private + */ +goog.async.Debouncer.prototype.onTimer_ = function() { + // There is a newer call to fire() within the debounce interval. + // Reschedule the callback and return. + if (this.refireAt_) { + this.timer_ = + goog.Timer.callOnce(this.callback_, this.refireAt_ - goog.now()); + this.refireAt_ = null; + return; + } + this.timer_ = null; + + if (!this.pauseCount_) { + this.doAction_(); + } else { + this.shouldFire_ = true; + } +}; + + +/** + * Calls the callback. + * @private + */ +goog.async.Debouncer.prototype.doAction_ = function() { + this.shouldFire_ = false; + this.listener_.apply(null, this.args_); +}; diff --git a/closure-library/closure/goog/async/delay.js b/closure-library/closure/goog/async/delay.js new file mode 100644 index 0000000000..17d50a298f --- /dev/null +++ b/closure-library/closure/goog/async/delay.js @@ -0,0 +1,193 @@ +// Copyright 2007 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 Defines a class useful for handling functions that must be + * invoked after a delay, especially when that delay is frequently restarted. + * Examples include delaying before displaying a tooltip, menu hysteresis, + * idle timers, etc. + * @author brenneman@google.com (Shawn Brenneman) + * @see ../demos/timers.html + */ + + +goog.provide('goog.Delay'); +goog.provide('goog.async.Delay'); + +goog.require('goog.Disposable'); +goog.require('goog.Timer'); + + + +/** + * A Delay object invokes the associated function after a specified delay. The + * interval duration can be specified once in the constructor, or can be defined + * each time the delay is started. Calling start on an active delay will reset + * the timer. + * + * @param {function(this:THIS)} listener Function to call when the + * delay completes. + * @param {number=} opt_interval The default length of the invocation delay (in + * milliseconds). + * @param {THIS=} opt_handler The object scope to invoke the function in. + * @template THIS + * @constructor + * @struct + * @extends {goog.Disposable} + * @final + */ +goog.async.Delay = function(listener, opt_interval, opt_handler) { + goog.async.Delay.base(this, 'constructor'); + + /** + * The function that will be invoked after a delay. + * @private {function(this:THIS)} + */ + this.listener_ = listener; + + /** + * The default amount of time to delay before invoking the callback. + * @type {number} + * @private + */ + this.interval_ = opt_interval || 0; + + /** + * The object context to invoke the callback in. + * @type {Object|undefined} + * @private + */ + this.handler_ = opt_handler; + + + /** + * Cached callback function invoked when the delay finishes. + * @type {Function} + * @private + */ + this.callback_ = goog.bind(this.doAction_, this); +}; +goog.inherits(goog.async.Delay, goog.Disposable); + + + +/** + * A deprecated alias. + * @deprecated Use goog.async.Delay instead. + * @constructor + * @final + */ +goog.Delay = goog.async.Delay; + + +/** + * Identifier of the active delay timeout, or 0 when inactive. + * @type {number} + * @private + */ +goog.async.Delay.prototype.id_ = 0; + + +/** + * Disposes of the object, cancelling the timeout if it is still outstanding and + * removing all object references. + * @override + * @protected + */ +goog.async.Delay.prototype.disposeInternal = function() { + goog.async.Delay.base(this, 'disposeInternal'); + this.stop(); + delete this.listener_; + delete this.handler_; +}; + + +/** + * Starts the delay timer. The provided listener function will be called after + * the specified interval. Calling start on an active timer will reset the + * delay interval. + * @param {number=} opt_interval If specified, overrides the object's default + * interval with this one (in milliseconds). + */ +goog.async.Delay.prototype.start = function(opt_interval) { + this.stop(); + this.id_ = goog.Timer.callOnce( + this.callback_, goog.isDef(opt_interval) ? opt_interval : this.interval_); +}; + + +/** + * Starts the delay timer if it's not already active. + * @param {number=} opt_interval If specified and the timer is not already + * active, overrides the object's default interval with this one (in + * milliseconds). + */ +goog.async.Delay.prototype.startIfNotActive = function(opt_interval) { + if (!this.isActive()) { + this.start(opt_interval); + } +}; + + +/** + * Stops the delay timer if it is active. No action is taken if the timer is not + * in use. + */ +goog.async.Delay.prototype.stop = function() { + if (this.isActive()) { + goog.Timer.clear(this.id_); + } + this.id_ = 0; +}; + + +/** + * Fires delay's action even if timer has already gone off or has not been + * started yet; guarantees action firing. Stops the delay timer. + */ +goog.async.Delay.prototype.fire = function() { + this.stop(); + this.doAction_(); +}; + + +/** + * Fires delay's action only if timer is currently active. Stops the delay + * timer. + */ +goog.async.Delay.prototype.fireIfActive = function() { + if (this.isActive()) { + this.fire(); + } +}; + + +/** + * @return {boolean} True if the delay is currently active, false otherwise. + */ +goog.async.Delay.prototype.isActive = function() { + return this.id_ != 0; +}; + + +/** + * Invokes the callback function after the delay successfully completes. + * @private + */ +goog.async.Delay.prototype.doAction_ = function() { + this.id_ = 0; + if (this.listener_) { + this.listener_.call(this.handler_); + } +}; diff --git a/closure-library/closure/goog/async/freelist.js b/closure-library/closure/goog/async/freelist.js new file mode 100644 index 0000000000..c58ddf704b --- /dev/null +++ b/closure-library/closure/goog/async/freelist.js @@ -0,0 +1,83 @@ +// 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) { + /** @private @const {number} */ + this.limit_ = limit; + /** @private @const {function()} */ + this.create_ = create; + /** @private @const {function(ITEM):void} */ + this.reset_ = reset; + + /** @private {number} */ + this.occupants_ = 0; + /** @private {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/closure-library/closure/goog/async/nexttick.js b/closure-library/closure/goog/async/nexttick.js new file mode 100644 index 0000000000..941c135b49 --- /dev/null +++ b/closure-library/closure/goog/async/nexttick.js @@ -0,0 +1,267 @@ +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Provides a function to schedule running a function as soon + * as possible after the current JS execution stops and yields to the event + * loop. + * + */ + +goog.provide('goog.async.nextTick'); +goog.provide('goog.async.throwException'); + +goog.require('goog.debug.entryPointRegistry'); +goog.require('goog.dom.TagName'); +goog.require('goog.functions'); +goog.require('goog.labs.userAgent.browser'); +goog.require('goog.labs.userAgent.engine'); + + +/** + * Throw an item without interrupting the current execution context. For + * example, if processing a group of items in a loop, sometimes it is useful + * to report an error while still allowing the rest of the batch to be + * processed. + * @param {*} exception + */ +goog.async.throwException = function(exception) { + // Each throw needs to be in its own context. + goog.global.setTimeout(function() { throw exception; }, 0); +}; + + +/** + * Fires the provided callbacks as soon as possible after the current JS + * execution context. setTimeout(…, 0) takes at least 4ms when called from + * within another setTimeout(…, 0) for legacy reasons. + * + * This will not schedule the callback as a microtask (i.e. a task that can + * preempt user input or networking callbacks). It is meant to emulate what + * setTimeout(_, 0) would do if it were not throttled. If you desire microtask + * behavior, use {@see goog.Promise} instead. + * + * @param {function(this:SCOPE)} callback Callback function to fire as soon as + * possible. + * @param {SCOPE=} opt_context Object in whose scope to call the listener. + * @param {boolean=} opt_useSetImmediate Avoid the IE workaround that + * ensures correctness at the cost of speed. See comments for details. + * @template SCOPE + */ +goog.async.nextTick = function(callback, opt_context, opt_useSetImmediate) { + var cb = callback; + if (opt_context) { + cb = goog.bind(callback, opt_context); + } + cb = goog.async.nextTick.wrapCallback_(cb); + // 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. See useSetImmediate_ + // for details. + if (goog.isFunction(goog.global.setImmediate) && + (opt_useSetImmediate || goog.async.nextTick.useSetImmediate_())) { + goog.global.setImmediate(cb); + return; + } + + // Look for and cache the custom fallback version of setImmediate. + if (!goog.async.nextTick.setImmediate_) { + goog.async.nextTick.setImmediate_ = + goog.async.nextTick.getSetImmediateEmulator_(); + } + goog.async.nextTick.setImmediate_(cb); +}; + + +/** + * Returns whether should use setImmediate implementation currently on window. + * + * window.setImmediate was introduced and currently only supported by IE10+, + * but due to a bug in the implementation it is not guaranteed that + * setImmediate is faster than setTimeout nor that setImmediate N is before + * setImmediate N+1. That is why we do not use the native version if + * available. We do, however, call setImmediate if it is a non-native function + * because that indicates that it has been replaced by goog.testing.MockClock + * which we do want to support. + * See + * http://connect.microsoft.com/IE/feedback/details/801823/setimmediate-and-messagechannel-are-broken-in-ie10 + * + * @return {boolean} Whether to use the implementation of setImmediate defined + * on Window. + * @private + * @suppress {missingProperties} For "Window.prototype.setImmediate" + */ +goog.async.nextTick.useSetImmediate_ = function() { + // Not a browser environment. + if (!goog.global.Window || !goog.global.Window.prototype) { + return true; + } + + // MS Edge has window.setImmediate natively, but it's not on Window.prototype. + // Also, there's no clean way to detect if the goog.global.setImmediate has + // been replaced by mockClock as its replacement also shows up as "[native + // code]" when using toString. Therefore, just always use + // goog.global.setImmediate for Edge. It's unclear if it suffers the same + // issues as IE10/11, but based on + // https://dev.modern.ie/testdrive/demos/setimmediatesorting/ + // it seems they've been working to ensure it's WAI. + if (goog.labs.userAgent.browser.isEdge() || + goog.global.Window.prototype.setImmediate != goog.global.setImmediate) { + // Something redefined setImmediate in which case we decide to use it (This + // is so that we use the mockClock setImmediate). + return true; + } + + return false; +}; + + +/** + * Cache for the setImmediate implementation. + * @type {function(function())} + * @private + */ +goog.async.nextTick.setImmediate_; + + +/** + * Determines the best possible implementation to run a function as soon as + * the JS event loop is idle. + * @return {function(function())} The "setImmediate" implementation. + * @private + */ +goog.async.nextTick.getSetImmediateEmulator_ = function() { + // Create a private message channel and use it to postMessage empty messages + // to ourselves. + /** @type {!Function|undefined} */ + var Channel = goog.global['MessageChannel']; + // If MessageChannel is not available and we are in a browser, implement + // an iframe based polyfill in browsers that have postMessage and + // document.addEventListener. The latter excludes IE8 because it has a + // synchronous postMessage implementation. + if (typeof Channel === 'undefined' && typeof window !== 'undefined' && + window.postMessage && window.addEventListener && + // Presto (The old pre-blink Opera engine) has problems with iframes + // and contentWindow. + !goog.labs.userAgent.engine.isPresto()) { + /** @constructor */ + Channel = function() { + // Make an empty, invisible iframe. + var iframe = /** @type {!HTMLIFrameElement} */ ( + document.createElement(String(goog.dom.TagName.IFRAME))); + iframe.style.display = 'none'; + iframe.src = ''; + document.documentElement.appendChild(iframe); + var win = iframe.contentWindow; + var doc = win.document; + doc.open(); + doc.write(''); + doc.close(); + // Do not post anything sensitive over this channel, as the workaround for + // pages with file: origin could allow that information to be modified or + // intercepted. + var message = 'callImmediate' + Math.random(); + // The same origin policy rejects attempts to postMessage from file: urls + // unless the origin is '*'. + var origin = win.location.protocol == 'file:' ? + '*' : + win.location.protocol + '//' + win.location.host; + var onmessage = goog.bind(function(e) { + // Validate origin and message to make sure that this message was + // intended for us. If the origin is set to '*' (see above) only the + // message needs to match since, for example, '*' != 'file://'. Allowing + // the wildcard is ok, as we are not concerned with security here. + if ((origin != '*' && e.origin != origin) || e.data != message) { + return; + } + this['port1'].onmessage(); + }, this); + win.addEventListener('message', onmessage, false); + this['port1'] = {}; + this['port2'] = { + postMessage: function() { win.postMessage(message, origin); } + }; + }; + } + if (typeof Channel !== 'undefined' && !goog.labs.userAgent.browser.isIE()) { + // Exclude all of IE due to + // http://codeforhire.com/2013/09/21/setimmediate-and-messagechannel-broken-on-internet-explorer-10/ + // which allows starving postMessage with a busy setTimeout loop. + // This currently affects IE10 and IE11 which would otherwise be able + // to use the postMessage based fallbacks. + var channel = new Channel(); + // Use a fifo linked list to call callbacks in the right order. + var head = {}; + var tail = head; + channel['port1'].onmessage = function() { + if (goog.isDef(head.next)) { + head = head.next; + var cb = head.cb; + head.cb = null; + cb(); + } + }; + return function(cb) { + tail.next = {cb: cb}; + tail = tail.next; + channel['port2'].postMessage(0); + }; + } + // Implementation for IE6 to IE10: Script elements fire an asynchronous + // onreadystatechange event when inserted into the DOM. + if (typeof document !== 'undefined' && + 'onreadystatechange' in + document.createElement(String(goog.dom.TagName.SCRIPT))) { + return function(cb) { + var script = /** @type {!HTMLScriptElement} */ ( + document.createElement(String(goog.dom.TagName.SCRIPT))); + script.onreadystatechange = function() { + // Clean up and call the callback. + script.onreadystatechange = null; + script.parentNode.removeChild(script); + script = null; + cb(); + cb = null; + }; + document.documentElement.appendChild(script); + }; + } + // Fall back to setTimeout with 0. In browsers this creates a delay of 5ms + // or more. + // NOTE(user): This fallback is used for IE11. + return function(cb) { + goog.global.setTimeout(/** @type {function()} */ (cb), 0); + }; +}; + + +/** + * Helper function that is overrided to protect callbacks with entry point + * monitor if the application monitors entry points. + * @param {function()} callback Callback function to fire as soon as possible. + * @return {function()} The wrapped callback. + * @private + */ +goog.async.nextTick.wrapCallback_ = goog.functions.identity; + + +// Register the callback function as an entry point, so that it can be +// monitored for exception handling, etc. This has to be done in this file +// since it requires special code to handle all browsers. +goog.debug.entryPointRegistry.register( + /** + * @param {function(!Function): !Function} transformer The transforming + * function. + */ + function(transformer) { goog.async.nextTick.wrapCallback_ = transformer; }); diff --git a/closure-library/closure/goog/async/run.js b/closure-library/closure/goog/async/run.js new file mode 100644 index 0000000000..09de192483 --- /dev/null +++ b/closure-library/closure/goog/async/run.js @@ -0,0 +1,141 @@ +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.async.run'); + +goog.require('goog.async.WorkQueue'); +goog.require('goog.async.nextTick'); +goog.require('goog.async.throwException'); + +/** + * @define {boolean} If true, use the global Promise to implement goog.async.run + * assuming either the native, or polyfill version will be used. Does still + * permit tests to use forceNextTick. + */ +goog.define('goog.ASSUME_NATIVE_PROMISE', false); + +/** + * Fires the provided callback just before the current callstack unwinds, or as + * soon as possible after the current JS execution context. + * @param {function(this:THIS)} callback + * @param {THIS=} opt_context Object to use as the "this value" when calling + * the provided function. + * @template THIS + */ +goog.async.run = function(callback, opt_context) { + if (!goog.async.run.schedule_) { + goog.async.run.initializeRunner_(); + } + if (!goog.async.run.workQueueScheduled_) { + // Nothing is currently scheduled, schedule it now. + goog.async.run.schedule_(); + goog.async.run.workQueueScheduled_ = true; + } + + goog.async.run.workQueue_.add(callback, opt_context); +}; + + +/** + * Initializes the function to use to process the work queue. + * @private + */ +goog.async.run.initializeRunner_ = function() { + if (goog.ASSUME_NATIVE_PROMISE || + (goog.global.Promise && goog.global.Promise.resolve)) { + // Use goog.global.Promise instead of just Promise because the relevant + // externs may be missing, and don't alias it because this could confuse the + // compiler into thinking the polyfill is required when it should be treated + // as optional. + var promise = goog.global.Promise.resolve(undefined); + goog.async.run.schedule_ = function() { + promise.then(goog.async.run.processWorkQueue); + }; + } else { + goog.async.run.schedule_ = function() { + goog.async.nextTick(goog.async.run.processWorkQueue); + }; + } +}; + + +/** + * Forces goog.async.run to use nextTick instead of Promise. + * + * This should only be done in unit tests. It's useful because MockClock + * replaces nextTick, but not the browser Promise implementation, so it allows + * Promise-based code to be tested with MockClock. + * + * However, we also want to run promises if the MockClock is no longer in + * control so we schedule a backup "setTimeout" to the unmocked timeout if + * provided. + * + * @param {function(function())=} opt_realSetTimeout + */ +goog.async.run.forceNextTick = function(opt_realSetTimeout) { + goog.async.run.schedule_ = function() { + goog.async.nextTick(goog.async.run.processWorkQueue); + if (opt_realSetTimeout) { + opt_realSetTimeout(goog.async.run.processWorkQueue); + } + }; +}; + + +/** + * The function used to schedule work asynchronousely. + * @private {function()} + */ +goog.async.run.schedule_; + + +/** @private {boolean} */ +goog.async.run.workQueueScheduled_ = false; + + +/** @private {!goog.async.WorkQueue} */ +goog.async.run.workQueue_ = new goog.async.WorkQueue(); + + +if (goog.DEBUG) { + /** + * Reset the work queue. Only available for tests in debug mode. + */ + goog.async.run.resetQueue = function() { + goog.async.run.workQueueScheduled_ = false; + goog.async.run.workQueue_ = new goog.async.WorkQueue(); + }; +} + + +/** + * Run any pending goog.async.run work items. This function is not intended + * for general use, but for use by entry point handlers to run items ahead of + * goog.async.nextTick. + */ +goog.async.run.processWorkQueue = function() { + // NOTE: additional work queue items may be added while processing. + var item = null; + while (item = goog.async.run.workQueue_.remove()) { + try { + item.fn.call(item.scope); + } catch (e) { + goog.async.throwException(e); + } + goog.async.run.workQueue_.returnUnused(item); + } + + // There are no more work items, allow processing to be scheduled again. + goog.async.run.workQueueScheduled_ = false; +}; diff --git a/closure-library/closure/goog/async/throttle.js b/closure-library/closure/goog/async/throttle.js new file mode 100644 index 0000000000..cb9f1221f0 --- /dev/null +++ b/closure-library/closure/goog/async/throttle.js @@ -0,0 +1,198 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Definition of the goog.async.Throttle class. + * + * @see ../demos/timers.html + */ + +goog.provide('goog.Throttle'); +goog.provide('goog.async.Throttle'); + +goog.require('goog.Disposable'); +goog.require('goog.Timer'); + + + +/** + * Throttle will perform an action that is passed in no more than once + * per interval (specified in milliseconds). If it gets multiple signals + * to perform the action while it is waiting, it will only perform the action + * once at the end of the interval. + * @param {function(this: T, ...?)} listener Function to callback when the + * action is triggered. + * @param {number} interval Interval over which to throttle. The listener can + * only be called once per interval. + * @param {T=} opt_handler Object in whose scope to call the listener. + * @constructor + * @struct + * @extends {goog.Disposable} + * @final + * @template T + */ +goog.async.Throttle = function(listener, interval, opt_handler) { + goog.async.Throttle.base(this, 'constructor'); + + /** + * Function to callback + * @type {function(this: T, ...?)} + * @private + */ + this.listener_ = + opt_handler != null ? goog.bind(listener, opt_handler) : listener; + + /** + * Interval for the throttle time + * @type {number} + * @private + */ + this.interval_ = interval; + + /** + * Cached callback function invoked after the throttle timeout completes + * @type {Function} + * @private + */ + this.callback_ = goog.bind(this.onTimer_, this); + + /** + * The last arguments passed into `fire`. + * @private {!IArrayLike} + */ + this.args_ = []; +}; +goog.inherits(goog.async.Throttle, goog.Disposable); + + + +/** + * A deprecated alias. + * @deprecated Use goog.async.Throttle instead. + * @constructor + * @final + */ +goog.Throttle = goog.async.Throttle; + + +/** + * Indicates that the action is pending and needs to be fired. + * @type {boolean} + * @private + */ +goog.async.Throttle.prototype.shouldFire_ = false; + + +/** + * Indicates the count of nested pauses currently in effect on the throttle. + * When this count is not zero, fired actions will be postponed until the + * throttle is resumed enough times to drop the pause count to zero. + * @type {number} + * @private + */ +goog.async.Throttle.prototype.pauseCount_ = 0; + + +/** + * Timer for scheduling the next callback + * @type {?number} + * @private + */ +goog.async.Throttle.prototype.timer_ = null; + + +/** + * Notifies the throttle that the action has happened. It will throttle the call + * so that the callback is not called too often according to the interval + * parameter passed to the constructor, passing the arguments from the last call + * of this function into the throttled function. + * @param {...?} var_args Arguments to pass on to the throttled function. + */ +goog.async.Throttle.prototype.fire = function(var_args) { + this.args_ = arguments; + if (!this.timer_ && !this.pauseCount_) { + this.doAction_(); + } else { + this.shouldFire_ = true; + } +}; + + +/** + * Cancels any pending action callback. The throttle can be restarted by + * calling {@link #fire}. + */ +goog.async.Throttle.prototype.stop = function() { + if (this.timer_) { + goog.Timer.clear(this.timer_); + this.timer_ = null; + this.shouldFire_ = false; + this.args_ = []; + } +}; + + +/** + * Pauses the throttle. All pending and future action callbacks will be + * delayed until the throttle is resumed. Pauses can be nested. + */ +goog.async.Throttle.prototype.pause = function() { + this.pauseCount_++; +}; + + +/** + * Resumes the throttle. If doing so drops the pausing count to zero, pending + * action callbacks will be executed as soon as possible, but still no sooner + * than an interval's delay after the previous call. Future action callbacks + * will be executed as normal. + */ +goog.async.Throttle.prototype.resume = function() { + this.pauseCount_--; + if (!this.pauseCount_ && this.shouldFire_ && !this.timer_) { + this.shouldFire_ = false; + this.doAction_(); + } +}; + + +/** @override */ +goog.async.Throttle.prototype.disposeInternal = function() { + goog.async.Throttle.base(this, 'disposeInternal'); + this.stop(); +}; + + +/** + * Handler for the timer to fire the throttle + * @private + */ +goog.async.Throttle.prototype.onTimer_ = function() { + this.timer_ = null; + + if (this.shouldFire_ && !this.pauseCount_) { + this.shouldFire_ = false; + this.doAction_(); + } +}; + + +/** + * Calls the callback + * @private + */ +goog.async.Throttle.prototype.doAction_ = function() { + this.timer_ = goog.Timer.callOnce(this.callback_, this.interval_); + this.listener_.apply(null, this.args_); +}; diff --git a/closure-library/closure/goog/async/workqueue.js b/closure-library/closure/goog/async/workqueue.js new file mode 100644 index 0000000000..d2b4138675 --- /dev/null +++ b/closure-library/closure/goog/async/workqueue.js @@ -0,0 +1,138 @@ +// 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/closure-library/closure/goog/base.js b/closure-library/closure/goog/base.js new file mode 100644 index 0000000000..ff3551aa33 --- /dev/null +++ b/closure-library/closure/goog/base.js @@ -0,0 +1,4057 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Bootstrap for the Google JS Library (Closure). + * + * In uncompiled mode base.js will attempt to load Closure's deps file, unless + * the global CLOSURE_NO_DEPS is set to true. This allows projects + * to include their own deps file(s) from different locations. + * + * Avoid including base.js more than once. This is strictly discouraged and not + * supported. goog.require(...) won't work properly in that case. + * + * @provideGoog + */ + + +/** + * @define {boolean} Overridden to true by the compiler. + */ +var COMPILED = false; + + +/** + * Base namespace for the Closure library. Checks to see goog is already + * defined in the current scope before assigning to prevent clobbering if + * base.js is loaded more than once. + * + * @const + */ +var goog = goog || {}; + +/** + * Reference to the global context. In most cases this will be 'window'. + * @const + * @suppress {newCheckTypes} + */ +goog.global = this; + + +/** + * A hook for overriding the define values in uncompiled mode. + * + * In uncompiled mode, `CLOSURE_UNCOMPILED_DEFINES` may be defined before + * loading base.js. If a key is defined in `CLOSURE_UNCOMPILED_DEFINES`, + * `goog.define` will use the value instead of the default value. This + * allows flags to be overwritten without compilation (this is normally + * accomplished with the compiler's "define" flag). + * + * Example: + *
+ *   var CLOSURE_UNCOMPILED_DEFINES = {'goog.DEBUG': false};
+ * 
+ * + * @type {Object|undefined} + */ +goog.global.CLOSURE_UNCOMPILED_DEFINES; + + +/** + * A hook for overriding the define values in uncompiled or compiled mode, + * like CLOSURE_UNCOMPILED_DEFINES but effective in compiled code. In + * uncompiled code CLOSURE_UNCOMPILED_DEFINES takes precedence. + * + * Also unlike CLOSURE_UNCOMPILED_DEFINES the values must be number, boolean or + * string literals or the compiler will emit an error. + * + * While any @define value may be set, only those set with goog.define will be + * effective for uncompiled code. + * + * Example: + *
+ *   var CLOSURE_DEFINES = {'goog.DEBUG': false} ;
+ * 
+ * + * @type {Object|undefined} + */ +goog.global.CLOSURE_DEFINES; + + +/** + * Returns true if the specified value is not undefined. + * + * @param {?} val Variable to test. + * @return {boolean} Whether variable is defined. + */ +goog.isDef = function(val) { + // void 0 always evaluates to undefined and hence we do not need to depend on + // the definition of the global variable named 'undefined'. + return val !== void 0; +}; + +/** + * Returns true if the specified value is a string. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is a string. + */ +goog.isString = function(val) { + return typeof val == 'string'; +}; + + +/** + * Returns true if the specified value is a boolean. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is boolean. + */ +goog.isBoolean = function(val) { + return typeof val == 'boolean'; +}; + + +/** + * Returns true if the specified value is a number. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is a number. + */ +goog.isNumber = function(val) { + return typeof val == 'number'; +}; + + +/** + * Builds an object structure for the provided namespace path, ensuring that + * names that already exist are not overwritten. For example: + * "a.b.c" -> a = {};a.b={};a.b.c={}; + * Used by goog.provide and goog.exportSymbol. + * @param {string} name name of the object that this file defines. + * @param {*=} opt_object the object to expose at the end of the path. + * @param {Object=} opt_objectToExportTo The object to add the path to; default + * is `goog.global`. + * @private + */ +goog.exportPath_ = function(name, opt_object, opt_objectToExportTo) { + var parts = name.split('.'); + var cur = opt_objectToExportTo || goog.global; + + // Internet Explorer exhibits strange behavior when throwing errors from + // methods externed in this manner. See the testExportSymbolExceptions in + // base_test.html for an example. + if (!(parts[0] in cur) && typeof cur.execScript != 'undefined') { + cur.execScript('var ' + parts[0]); + } + + for (var part; parts.length && (part = parts.shift());) { + if (!parts.length && goog.isDef(opt_object)) { + // last part and we have an object; use it + cur[part] = opt_object; + } else if (cur[part] && cur[part] !== Object.prototype[part]) { + cur = cur[part]; + } else { + cur = cur[part] = {}; + } + } +}; + + +/** + * Defines a named value. In uncompiled mode, the value is retrieved from + * CLOSURE_DEFINES or CLOSURE_UNCOMPILED_DEFINES if the object is defined and + * has the property specified, and otherwise used the defined defaultValue. + * When compiled the default can be overridden using the compiler options or the + * value set in the CLOSURE_DEFINES object. Returns the defined value so that it + * can be used safely in modules. + * + * @param {string} name The distinguished name to provide. + * @param {string|number|boolean} defaultValue + * @return {string|number|boolean} The defined value. + */ +goog.define = function(name, defaultValue) { + var value = defaultValue; + if (!COMPILED) { + var uncompiledDefines = goog.global.CLOSURE_UNCOMPILED_DEFINES; + var defines = goog.global.CLOSURE_DEFINES; + if (uncompiledDefines && + // Anti DOM-clobbering runtime check (b/37736576). + /** @type {?} */ (uncompiledDefines).nodeType === undefined && + Object.prototype.hasOwnProperty.call(uncompiledDefines, name)) { + value = uncompiledDefines[name]; + } else if ( + defines && + // Anti DOM-clobbering runtime check (b/37736576). + /** @type {?} */ (defines).nodeType === undefined && + Object.prototype.hasOwnProperty.call(defines, name)) { + value = defines[name]; + } + } + goog.exportPath_(name, value); + return value; +}; + + +/** + * @define {boolean} DEBUG is provided as a convenience so that debugging code + * that should not be included in a production. It can be easily stripped + * by specifying --define goog.DEBUG=false to the Closure Compiler aka + * JSCompiler. For example, most toString() methods should be declared inside an + * "if (goog.DEBUG)" conditional because they are generally used for debugging + * purposes and it is difficult for the JSCompiler to statically determine + * whether they are used. + */ +goog.define('goog.DEBUG', true); + + +/** + * @define {string} LOCALE defines the locale being used for compilation. It is + * used to select locale specific data to be compiled in js binary. BUILD rule + * can specify this value by "--define goog.LOCALE=" as a compiler + * option. + * + * Take into account that the locale code format is important. You should use + * the canonical Unicode format with hyphen as a delimiter. Language must be + * lowercase, Language Script - Capitalized, Region - UPPERCASE. + * There are few examples: pt-BR, en, en-US, sr-Latin-BO, zh-Hans-CN. + * + * See more info about locale codes here: + * http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers + * + * For language codes you should use values defined by ISO 693-1. See it here + * http://www.w3.org/WAI/ER/IG/ert/iso639.htm. There is only one exception from + * this rule: the Hebrew language. For legacy reasons the old code (iw) should + * be used instead of the new code (he). + * + */ +goog.define('goog.LOCALE', 'en'); // default to en + + +/** + * @define {boolean} Whether this code is running on trusted sites. + * + * On untrusted sites, several native functions can be defined or overridden by + * external libraries like Prototype, Datejs, and JQuery and setting this flag + * to false forces closure to use its own implementations when possible. + * + * If your JavaScript can be loaded by a third party site and you are wary about + * relying on non-standard implementations, specify + * "--define goog.TRUSTED_SITE=false" to the compiler. + */ +goog.define('goog.TRUSTED_SITE', true); + + +/** + * @define {boolean} Whether a project is expected to be running in strict mode. + * + * This define can be used to trigger alternate implementations compatible with + * running in EcmaScript Strict mode or warn about unavailable functionality. + * @see https://goo.gl/PudQ4y + * + */ +goog.define('goog.STRICT_MODE_COMPATIBLE', false); + + +/** + * @define {boolean} Whether code that calls {@link goog.setTestOnly} should + * be disallowed in the compilation unit. + */ +goog.define('goog.DISALLOW_TEST_ONLY_CODE', COMPILED && !goog.DEBUG); + + +/** + * @define {boolean} Whether to use a Chrome app CSP-compliant method for + * loading scripts via goog.require. @see appendScriptSrcNode_. + */ +goog.define('goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING', false); + + +/** + * Defines a namespace in Closure. + * + * A namespace may only be defined once in a codebase. It may be defined using + * goog.provide() or goog.module(). + * + * The presence of one or more goog.provide() calls in a file indicates + * that the file defines the given objects/namespaces. + * Provided symbols must not be null or undefined. + * + * In addition, goog.provide() creates the object stubs for a namespace + * (for example, goog.provide("goog.foo.bar") will create the object + * goog.foo.bar if it does not already exist). + * + * Build tools also scan for provide/require/module statements + * to discern dependencies, build dependency files (see deps.js), etc. + * + * @see goog.require + * @see goog.module + * @param {string} name Namespace provided by this file in the form + * "goog.package.part". + */ +goog.provide = function(name) { + if (goog.isInModuleLoader_()) { + throw new Error('goog.provide cannot be used within a module.'); + } + if (!COMPILED) { + // Ensure that the same namespace isn't provided twice. + // A goog.module/goog.provide maps a goog.require to a specific file + if (goog.isProvided_(name)) { + throw new 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; + while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) { + if (goog.getObjectByName(namespace)) { + break; + } + goog.implicitNamespaces_[namespace] = true; + } + } + + goog.exportPath_(name, opt_obj); +}; + + +/** + * Returns CSP nonce, if set for any script tag. + * @param {?Window=} opt_window The window context used to retrieve the nonce. + * Defaults to global context. + * @return {string} CSP nonce or empty string if no nonce is present. + */ +goog.getScriptNonce = function(opt_window) { + if (opt_window && opt_window != goog.global) { + return goog.getScriptNonce_(opt_window.document); + } + if (goog.cspNonce_ === null) { + goog.cspNonce_ = goog.getScriptNonce_(goog.global.document); + } + return goog.cspNonce_; +}; + + +/** + * According to the CSP3 spec a nonce must be a valid base64 string. + * @see https://www.w3.org/TR/CSP3/#grammardef-base64-value + * @private @const + */ +goog.NONCE_PATTERN_ = /^[\w+/_-]+[=]{0,2}$/; + + +/** + * @private {?string} + */ +goog.cspNonce_ = null; + + +/** + * Returns CSP nonce, if set for any script tag. + * @param {!Document} doc + * @return {string} CSP nonce or empty string if no nonce is present. + * @private + */ +goog.getScriptNonce_ = function(doc) { + var script = doc.querySelector && doc.querySelector('script[nonce]'); + if (script) { + // Try to get the nonce from the IDL property first, because browsers that + // implement additional nonce protection features (currently only Chrome) to + // prevent nonce stealing via CSS do not expose the nonce via attributes. + // See https://github.com/whatwg/html/issues/2369 + var nonce = script['nonce'] || script.getAttribute('nonce'); + if (nonce && goog.NONCE_PATTERN_.test(nonce)) { + return nonce; + } + } + return ''; +}; + + +/** + * Module identifier validation regexp. + * Note: This is a conservative check, it is very possible to be more lenient, + * the primary exclusion here is "/" and "\" and a leading ".", these + * restrictions are intended to leave the door open for using goog.require + * with relative file paths rather than module identifiers. + * @private + */ +goog.VALID_MODULE_RE_ = /^[a-zA-Z_$][a-zA-Z0-9._$]*$/; + + +/** + * Defines a module in Closure. + * + * Marks that this file must be loaded as a module and claims the namespace. + * + * A namespace may only be defined once in a codebase. It may be defined using + * goog.provide() or goog.module(). + * + * goog.module() has three requirements: + * - goog.module may not be used in the same file as goog.provide. + * - goog.module must be the first statement in the file. + * - only one goog.module is allowed per file. + * + * When a goog.module annotated file is loaded, it is enclosed in + * a strict function closure. This means that: + * - any variables declared in a goog.module file are private to the file + * (not global), though the compiler is expected to inline the module. + * - The code must obey all the rules of "strict" JavaScript. + * - the file will be marked as "use strict" + * + * NOTE: unlike goog.provide, goog.module does not declare any symbols by + * itself. If declared symbols are desired, use + * goog.module.declareLegacyNamespace(). + * + * + * See the public goog.module proposal: http://goo.gl/Va1hin + * + * @param {string} name Namespace provided by this file in the form + * "goog.package.part", is expected but not required. + * @return {void} + */ +goog.module = function(name) { + if (!goog.isString(name) || !name || + name.search(goog.VALID_MODULE_RE_) == -1) { + throw new Error('Invalid module identifier'); + } + if (!goog.isInGoogModuleLoader_()) { + throw new Error( + 'Module ' + name + ' has been loaded incorrectly. Note, ' + + 'modules cannot be loaded as normal scripts. They require some kind of ' + + 'pre-processing step. You\'re likely trying to load a module via a ' + + 'script tag or as a part of a concatenated bundle without rewriting the ' + + 'module. For more info see: ' + + 'https://github.com/google/closure-library/wiki/goog.module:-an-ES6-module-like-alternative-to-goog.provide.'); + } + if (goog.moduleLoaderState_.moduleName) { + throw new Error('goog.module may only be called once per module.'); + } + + // Store the module name for the loader. + goog.moduleLoaderState_.moduleName = name; + if (!COMPILED) { + // Ensure that the same namespace isn't provided twice. + // A goog.module/goog.provide maps a goog.require to a specific file + if (goog.isProvided_(name)) { + throw new Error('Namespace "' + name + '" already declared.'); + } + delete goog.implicitNamespaces_[name]; + } +}; + + +/** + * @param {string} name The module identifier. + * @return {?} The module exports for an already loaded module or null. + * + * Note: This is not an alternative to goog.require, it does not + * indicate a hard dependency, instead it is used to indicate + * an optional dependency or to access the exports of a module + * that has already been loaded. + * @suppress {missingProvide} + */ +goog.module.get = function(name) { + + return goog.module.getInternal_(name); +}; + + +/** + * @param {string} name The module identifier. + * @return {?} The module exports for an already loaded module or null. + * @private + */ +goog.module.getInternal_ = function(name) { + if (!COMPILED) { + if (name in goog.loadedModules_) { + return goog.loadedModules_[name].exports; + } else if (!goog.implicitNamespaces_[name]) { + var ns = goog.getObjectByName(name); + return ns != null ? ns : null; + } + } + return null; +}; + + +/** + * Types of modules the debug loader can load. + * @enum {string} + */ +goog.ModuleType = { + ES6: 'es6', + GOOG: 'goog' +}; + + +/** + * @private {?{ + * moduleName: (string|undefined), + * declareLegacyNamespace:boolean, + * type: ?goog.ModuleType + * }} + */ +goog.moduleLoaderState_ = null; + + +/** + * @private + * @return {boolean} Whether a goog.module or an es6 module is currently being + * initialized. + */ +goog.isInModuleLoader_ = function() { + return goog.isInGoogModuleLoader_() || goog.isInEs6ModuleLoader_(); +}; + + +/** + * @private + * @return {boolean} Whether a goog.module is currently being initialized. + */ +goog.isInGoogModuleLoader_ = function() { + return !!goog.moduleLoaderState_ && + goog.moduleLoaderState_.type == goog.ModuleType.GOOG; +}; + + +/** + * @private + * @return {boolean} Whether an es6 module is currently being initialized. + */ +goog.isInEs6ModuleLoader_ = function() { + var inLoader = !!goog.moduleLoaderState_ && + goog.moduleLoaderState_.type == goog.ModuleType.ES6; + + if (inLoader) { + return true; + } + + var jscomp = goog.global['$jscomp']; + + if (jscomp) { + // jscomp may not have getCurrentModulePath if this is a compiled bundle + // that has some of the runtime, but not all of it. This can happen if + // optimizations are turned on so the unused runtime is removed but renaming + // and Closure pass are off (so $jscomp is still named $jscomp and the + // goog.provide/require calls still exist). + if (typeof jscomp.getCurrentModulePath != 'function') { + return false; + } + + // Bundled ES6 module. + return !!jscomp.getCurrentModulePath(); + } + + return false; +}; + + +/** + * 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.isInGoogModuleLoader_()) { + throw new Error( + 'goog.module.declareLegacyNamespace must be called from ' + + 'within a goog.module'); + } + if (!COMPILED && !goog.moduleLoaderState_.moduleName) { + throw new Error( + 'goog.module must be called prior to ' + + 'goog.module.declareLegacyNamespace.'); + } + goog.moduleLoaderState_.declareLegacyNamespace = true; +}; + + +/** + * Associates an ES6 module with a Closure module ID so that is available via + * goog.require. The associated ID acts like a goog.module ID - it does not + * create any global names, it is merely available via goog.require / + * goog.module.get / goog.forwardDeclare / goog.requireType. goog.require and + * goog.module.get will return the entire module as if it was import *'d. This + * allows Closure files to reference ES6 modules for the sake of migration. + * + * @param {string} namespace + * @suppress {missingProvide} + */ +goog.declareModuleId = function(namespace) { + if (!COMPILED) { + if (!goog.isInEs6ModuleLoader_()) { + throw new Error( + 'goog.declareModuleId may only be called from ' + + 'within an ES6 module'); + } + if (goog.moduleLoaderState_ && goog.moduleLoaderState_.moduleName) { + throw new Error( + 'goog.declareModuleId may only be called once per module.'); + } + if (namespace in goog.loadedModules_) { + throw new Error( + 'Module with namespace "' + namespace + '" already exists.'); + } + } + if (goog.moduleLoaderState_) { + // Not bundled - debug loading. + goog.moduleLoaderState_.moduleName = namespace; + } else { + // Bundled - not debug loading, no module loader state. + var jscomp = goog.global['$jscomp']; + if (!jscomp || typeof jscomp.getCurrentModulePath != 'function') { + throw new Error( + 'Module with namespace "' + namespace + + '" has been loaded incorrectly.'); + } + var exports = jscomp.require(jscomp.getCurrentModulePath()); + goog.loadedModules_[namespace] = { + exports: exports, + type: goog.ModuleType.ES6, + moduleId: namespace + }; + } +}; + + +/** + * Deprecated old name for goog.declareModuleId. This function is being renamed + * to help disambiguate with goog.module.declareLegacyNamespace. + * + * @type {function(string): undefined} + * @suppress {missingProvide} + */ +goog.module.declareNamespace = goog.declareModuleId; + + +/** + * Marks that the current file should only be used for testing, and never for + * live code in production. + * + * In the case of unit tests, the message may optionally be an exact namespace + * for the test (e.g. 'goog.stringTest'). The linter will then ignore the extra + * provide (if not explicitly defined in the code). + * + * @param {string=} opt_message Optional message to add to the error that's + * raised when used in production code. + */ +goog.setTestOnly = function(opt_message) { + if (goog.DISALLOW_TEST_ONLY_CODE) { + opt_message = opt_message || ''; + throw new Error( + 'Importing test-only code into non-debug environment' + + (opt_message ? ': ' + opt_message : '.')); + } +}; + + +/** + * Forward declares a symbol. This is an indication to the compiler that the + * symbol may be used in the source yet is not required and may not be provided + * in compilation. + * + * The most common usage of forward declaration is code that takes a type as a + * function parameter but does not need to require it. By forward declaring + * instead of requiring, no hard dependency is made, and (if not required + * elsewhere) the namespace may never be required and thus, not be pulled + * into the JavaScript binary. If it is required elsewhere, it will be type + * checked as normal. + * + * Before using goog.forwardDeclare, please read the documentation at + * https://github.com/google/closure-compiler/wiki/Bad-Type-Annotation to + * understand the options and tradeoffs when working with forward declarations. + * + * @param {string} name The namespace to forward declare in the form of + * "goog.package.part". + */ +goog.forwardDeclare = function(name) {}; + + +/** + * Forward declare type information. Used to assign types to goog.global + * referenced object that would otherwise result in unknown type references + * and thus block property disambiguation. + */ +goog.forwardDeclare('Document'); +goog.forwardDeclare('HTMLScriptElement'); +goog.forwardDeclare('XMLHttpRequest'); + + +if (!COMPILED) { + /** + * Check if the given name has been goog.provided. This will return false for + * names that are available only as implicit namespaces. + * @param {string} name name of the object to look for. + * @return {boolean} Whether the name has been provided. + * @private + */ + goog.isProvided_ = function(name) { + return (name in goog.loadedModules_) || + (!goog.implicitNamespaces_[name] && + goog.isDefAndNotNull(goog.getObjectByName(name))); + }; + + /** + * Namespaces implicitly defined by goog.provide. For example, + * goog.provide('goog.events.Event') implicitly declares that 'goog' and + * 'goog.events' must be namespaces. + * + * @type {!Object} + * @private + */ + goog.implicitNamespaces_ = {'goog.module': true}; + + // NOTE: We add goog.module as an implicit namespace as goog.module is defined + // here and because the existing module package has not been moved yet out of + // the goog.module namespace. This satisifies both the debug loader and + // ahead-of-time dependency management. +} + + +/** + * Returns an object based on its fully qualified external name. The object + * is not found if null or undefined. If you are using a compilation pass that + * renames property names beware that using this function will not find renamed + * properties. + * + * @param {string} name The fully qualified name. + * @param {Object=} opt_obj The object within which to look; default is + * |goog.global|. + * @return {?} The value (object or primitive) or, if not found, null. + */ +goog.getObjectByName = function(name, opt_obj) { + var parts = name.split('.'); + var cur = opt_obj || goog.global; + for (var i = 0; i < parts.length; i++) { + cur = cur[parts[i]]; + if (!goog.isDefAndNotNull(cur)) { + return null; + } + } + return cur; +}; + + +/** + * Globalizes a whole namespace, such as goog or goog.lang. + * + * @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. + */ +goog.globalize = function(obj, opt_global) { + var global = opt_global || goog.global; + for (var x in obj) { + global[x] = obj[x]; + } +}; + + +/** + * 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 {boolean|!Object=} opt_loadFlags Parameters indicating + * how the file must be loaded. The boolean 'true' is equivalent + * to {'module': 'goog'} for backwards-compatibility. Valid properties + * and values include {'module': 'goog'} and {'lang': 'es6'}. + */ +goog.addDependency = function(relPath, provides, requires, opt_loadFlags) { + if (!COMPILED && goog.DEPENDENCIES_ENABLED) { + goog.debugLoader_.addDependency(relPath, provides, requires, opt_loadFlags); + } +}; + + + + +// NOTE(nnaze): The debug DOM loader was included in base.js as an original way +// to do "debug-mode" development. The dependency system can sometimes be +// confusing, as can the debug DOM loader's asynchronous nature. +// +// With the DOM loader, a call to goog.require() is not blocking -- the script +// will not load until some point after the current script. If a namespace is +// needed at runtime, it needs to be defined in a previous script, or loaded via +// require() with its registered dependencies. +// +// User-defined namespaces may need their own deps file. For a reference on +// creating a deps file, see: +// Externally: https://developers.google.com/closure/library/docs/depswriter +// +// Because of legacy clients, the DOM loader can't be easily removed from +// base.js. Work was done to make it disableable or replaceable for +// different environments (DOM-less JavaScript interpreters like Rhino or V8, +// for example). See bootstrap/ for more information. + + +/** + * @define {boolean} Whether to enable the debug loader. + * + * If enabled, a call to goog.require() will attempt to load the namespace by + * appending a script tag to the DOM (if the namespace has been registered). + * + * If disabled, goog.require() will simply assert that the namespace has been + * provided (and depend on the fact that some outside tool correctly ordered + * the script). + */ +goog.define('goog.ENABLE_DEBUG_LOADER', true); + + +/** + * @param {string} msg + * @private + */ +goog.logToConsole_ = function(msg) { + if (goog.global.console) { + goog.global.console['error'](msg); + } +}; + + +/** + * Implements a system for the dynamic resolution of dependencies that works in + * parallel with the BUILD system. + * + * Note that all calls to goog.require will be stripped by the compiler. + * + * @see goog.provide + * @param {string} namespace Namespace (as was given in goog.provide, + * goog.module, or goog.declareModuleId) in the form + * "goog.package.part". + * @return {?} If called within a goog.module or ES6 module file, the associated + * namespace or module otherwise null. + */ +goog.require = function(namespace) { + if (!COMPILED) { + // Might need to lazy load on old IE. + if (goog.ENABLE_DEBUG_LOADER) { + goog.debugLoader_.requested(namespace); + } + + // If the object already exists we do not need to do anything. + if (goog.isProvided_(namespace)) { + if (goog.isInModuleLoader_()) { + return goog.module.getInternal_(namespace); + } + } else if (goog.ENABLE_DEBUG_LOADER) { + var moduleLoaderState = goog.moduleLoaderState_; + goog.moduleLoaderState_ = null; + try { + goog.debugLoader_.load_(namespace); + } finally { + goog.moduleLoaderState_ = moduleLoaderState; + } + } + + return null; + } +}; + + +/** + * Requires a symbol for its type information. This is an indication to the + * compiler that the symbol may appear in type annotations, yet it is not + * referenced at runtime. + * + * When called within a goog.module or ES6 module file, the return value may be + * assigned to or destructured into a variable, but it may not be otherwise used + * in code outside of a type annotation. + * + * Note that all calls to goog.requireType will be stripped by the compiler. + * + * @param {string} namespace Namespace (as was given in goog.provide, + * goog.module, or goog.declareModuleId) in the form + * "goog.package.part". + * @return {?} + */ +goog.requireType = function(namespace) { + // Return an empty object so that single-level destructuring of the return + // value doesn't crash at runtime when using the debug loader. Multi-level + // destructuring isn't supported. + return {}; +}; + + +/** + * Path for included scripts. + * @type {string} + */ +goog.basePath = ''; + + +/** + * A hook for overriding the base path. + * @type {string|undefined} + */ +goog.global.CLOSURE_BASE_PATH; + + +/** + * Whether to attempt to load Closure's deps file. By default, when uncompiled, + * deps files will attempt to be loaded. + * @type {boolean|undefined} + */ +goog.global.CLOSURE_NO_DEPS; + + +/** + * A function to import a single script. This is meant to be overridden when + * Closure is being run in non-HTML contexts, such as web workers. It's defined + * in the global scope so that it can be set before base.js is loaded, which + * allows deps.js to be imported properly. + * + * The first parameter the script source, which is a relative URI. The second, + * optional parameter is the script contents, in the event the script needed + * transformation. It should return true if the script was imported, false + * otherwise. + * @type {(function(string, string=): boolean)|undefined} + */ +goog.global.CLOSURE_IMPORT_SCRIPT; + + +/** + * Null function used for default values of callbacks, etc. + * @return {void} Nothing. + */ +goog.nullFunction = function() {}; + + +/** + * When defining a class Foo with an abstract method bar(), you can do: + * Foo.prototype.bar = goog.abstractMethod + * + * Now if a subclass of Foo fails to override bar(), an error will be thrown + * when bar() is invoked. + * + * @type {!Function} + * @throws {Error} when invoked to indicate the method should be overridden. + */ +goog.abstractMethod = function() { + throw new Error('unimplemented abstract method'); +}; + + +/** + * Adds a `getInstance` static method that always returns the same + * instance object. + * @param {!Function} ctor The constructor for the class to add the static + * method to. + * @suppress {missingProperties} 'instance_' isn't a property on 'Function' + * but we don't have a better type to use here. + */ +goog.addSingletonGetter = function(ctor) { + // instance_ is immediately set to prevent issues with sealed constructors + // such as are encountered when a constructor is returned as the export object + // of a goog.module in unoptimized code. + // Delcare type to avoid conformance violations that ctor.instance_ is unknown + /** @type {undefined|!Object} @suppress {underscore} */ + ctor.instance_ = undefined; + ctor.getInstance = function() { + if (ctor.instance_) { + return ctor.instance_; + } + if (goog.DEBUG) { + // NOTE: JSCompiler can't optimize away Array#push. + goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = ctor; + } + // Cast to avoid conformance violations that ctor.instance_ is unknown + return /** @type {!Object|undefined} */ (ctor.instance_) = new ctor; + }; +}; + + +/** + * All singleton classes that have been instantiated, for testing. Don't read + * it directly, use the `goog.testing.singleton` module. The compiler + * removes this variable if unused. + * @type {!Array} + * @private + */ +goog.instantiatedSingletons_ = []; + + +/** + * @define {boolean} Whether to load goog.modules using `eval` when using + * the debug loader. This provides a better debugging experience as the + * source is unmodified and can be edited using Chrome Workspaces or similar. + * However in some environments the use of `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 or path to module exports map. + * @private @const {!Object} + */ +goog.loadedModules_ = {}; + + +/** + * True if the debug loader enabled and used. + * @const {boolean} + */ +goog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER; + + +/** + * @define {string} How to decide whether to transpile. Valid values + * are 'always', 'never', and 'detect'. The default ('detect') is to + * use feature detection to determine which language levels need + * transpilation. + */ +// NOTE(sdh): we could expand this to accept a language level to bypass +// detection: e.g. goog.TRANSPILE == 'es5' would transpile ES6 files but +// would leave ES3 and ES5 files alone. +goog.define('goog.TRANSPILE', 'detect'); + +/** + * @define {boolean} If true assume that ES modules have already been + * transpiled by the jscompiler (in the same way that transpile.js would + * transpile them - to jscomp modules). Useful only for servers that wish to use + * the debug loader and transpile server side. Thus this is only respected if + * goog.TRANSPILE is "never". + */ +goog.define('goog.ASSUME_ES_MODULES_TRANSPILED', false); + + +/** + * @define {string} If a file needs to be transpiled what the output language + * should be. By default this is the highest language level this file detects + * the current environment supports. Generally this flag should not be set, but + * it could be useful to override. Example: If the current environment supports + * ES6 then by default ES7+ files will be transpiled to ES6, unless this is + * overridden. + * + * Valid values include: es3, es5, es6, es7, and es8. Anything not recognized + * is treated as es3. + * + * Note that setting this value does not force transpilation. Just if + * transpilation occurs this will be the output. So this is most useful when + * goog.TRANSPILE is set to 'always' and then forcing the language level to be + * something lower than what the environment detects. + */ +goog.define('goog.TRANSPILE_TO_LANGUAGE', ''); + + +/** + * @define {string} Path to the transpiler. Executing the script at this + * path (relative to base.js) should define a function $jscomp.transpile. + */ +goog.define('goog.TRANSPILER', 'transpile.js'); + + +/** + * @package {?boolean} + * Visible for testing. + */ +goog.hasBadLetScoping = null; + + +/** + * @return {boolean} + * @package Visible for testing. + */ +goog.useSafari10Workaround = function() { + if (goog.hasBadLetScoping == null) { + var hasBadLetScoping; + try { + hasBadLetScoping = !eval( + '"use strict";' + + 'let x = 1; function f() { return typeof x; };' + + 'f() == "number";'); + } catch (e) { + // Assume that ES6 syntax isn't supported. + hasBadLetScoping = false; + } + goog.hasBadLetScoping = hasBadLetScoping; + } + return goog.hasBadLetScoping; +}; + + +/** + * @param {string} moduleDef + * @return {string} + * @package Visible for testing. + */ +goog.workaroundSafari10EvalBug = function(moduleDef) { + return '(function(){' + moduleDef + + '\n' + // Terminate any trailing single line comment. + ';' + // Terminate any trailing expression. + '})();\n'; +}; + + +/** + * @param {function(?):?|string} moduleDef The module definition. + */ +goog.loadModule = function(moduleDef) { + // NOTE: we allow function definitions to be either in the from + // of a string to eval (which keeps the original source intact) or + // in a eval forbidden environment (CSP) we allow a function definition + // which in its body must call `goog.module`, and return the exports + // of the module. + var previousState = goog.moduleLoaderState_; + try { + goog.moduleLoaderState_ = { + moduleName: '', + declareLegacyNamespace: false, + type: goog.ModuleType.GOOG + }; + var exports; + if (goog.isFunction(moduleDef)) { + exports = moduleDef.call(undefined, {}); + } else if (goog.isString(moduleDef)) { + if (goog.useSafari10Workaround()) { + moduleDef = goog.workaroundSafari10EvalBug(moduleDef); + } + + exports = goog.loadModuleFromSource_.call(undefined, moduleDef); + } else { + throw new Error('Invalid module definition'); + } + + var moduleName = goog.moduleLoaderState_.moduleName; + if (goog.isString(moduleName) && moduleName) { + // Don't seal legacy namespaces as they may be used as a parent of + // another namespace + if (goog.moduleLoaderState_.declareLegacyNamespace) { + goog.constructNamespace_(moduleName, exports); + } else if ( + goog.SEAL_MODULE_EXPORTS && Object.seal && + typeof exports == 'object' && exports != null) { + Object.seal(exports); + } + + var data = { + exports: exports, + type: goog.ModuleType.GOOG, + moduleId: goog.moduleLoaderState_.moduleName + }; + goog.loadedModules_[moduleName] = data; + } else { + throw new Error('Invalid module name \"' + moduleName + '\"'); + } + } finally { + goog.moduleLoaderState_ = previousState; + } +}; + + +/** + * @private @const + */ +goog.loadModuleFromSource_ = /** @type {function(string):?} */ (function() { + // NOTE: we avoid declaring parameters or local variables here to avoid + // masking globals or leaking values into the module definition. + 'use strict'; + var exports = {}; + eval(arguments[0]); + return exports; +}); + + +/** + * Normalize a file path by removing redundant ".." and extraneous "." file + * path components. + * @param {string} path + * @return {string} + * @private + */ +goog.normalizePath_ = function(path) { + var components = path.split('/'); + var i = 0; + while (i < components.length) { + if (components[i] == '.') { + components.splice(i, 1); + } else if ( + i && components[i] == '..' && components[i - 1] && + components[i - 1] != '..') { + components.splice(--i, 2); + } else { + i++; + } + } + return components.join('/'); +}; + + +/** + * Provides a hook for loading a file when using Closure's goog.require() API + * with goog.modules. In particular this hook is provided to support Node.js. + * + * @type {(function(string):string)|undefined} + */ +goog.global.CLOSURE_LOAD_FILE_SYNC; + + +/** + * Loads file by synchronous XHR. Should not be used in production environments. + * @param {string} src Source URL. + * @return {?string} File contents, or null if load failed. + * @private + */ +goog.loadFileSync_ = function(src) { + if (goog.global.CLOSURE_LOAD_FILE_SYNC) { + return goog.global.CLOSURE_LOAD_FILE_SYNC(src); + } else { + try { + /** @type {XMLHttpRequest} */ + var xhr = new goog.global['XMLHttpRequest'](); + xhr.open('get', src, false); + xhr.send(); + // NOTE: Successful http: requests have a status of 200, but successful + // file: requests may have a status of zero. Any other status, or a + // thrown exception (particularly in case of file: requests) indicates + // some sort of error, which we treat as a missing or unavailable file. + return xhr.status == 0 || xhr.status == 200 ? xhr.responseText : null; + } catch (err) { + // No need to rethrow or log, since errors should show up on their own. + return null; + } + } +}; + + +/** + * Lazily retrieves the transpiler and applies it to the source. + * @param {string} code JS code. + * @param {string} path Path to the code. + * @param {string} target Language level output. + * @return {string} The transpiled code. + * @private + */ +goog.transpile_ = function(code, path, target) { + var jscomp = goog.global['$jscomp']; + if (!jscomp) { + goog.global['$jscomp'] = jscomp = {}; + } + var transpile = jscomp.transpile; + if (!transpile) { + var transpilerPath = goog.basePath + goog.TRANSPILER; + var transpilerCode = goog.loadFileSync_(transpilerPath); + if (transpilerCode) { + // This must be executed synchronously, since by the time we know we + // need it, we're about to load and write the ES6 code synchronously, + // so a normal script-tag load will be too slow. Wrapped in a function + // so that code is eval'd in the global scope. + (function() { + eval(transpilerCode + '\n//# sourceURL=' + transpilerPath); + }).call(goog.global); + // Even though the transpiler is optional, if $gwtExport is found, it's + // a sign the transpiler was loaded and the $jscomp.transpile *should* + // be there. + if (goog.global['$gwtExport'] && goog.global['$gwtExport']['$jscomp'] && + !goog.global['$gwtExport']['$jscomp']['transpile']) { + throw new Error( + 'The transpiler did not properly export the "transpile" ' + + 'method. $gwtExport: ' + JSON.stringify(goog.global['$gwtExport'])); + } + // transpile.js only exports a single $jscomp function, transpile. We + // grab just that and add it to the existing definition of $jscomp which + // contains the polyfills. + goog.global['$jscomp'].transpile = + goog.global['$gwtExport']['$jscomp']['transpile']; + jscomp = goog.global['$jscomp']; + transpile = jscomp.transpile; + } + } + if (!transpile) { + // The transpiler is an optional component. If it's not available then + // replace it with a pass-through function that simply logs. + var suffix = ' requires transpilation but no transpiler was found.'; + transpile = jscomp.transpile = function(code, path) { + // TODO(sdh): figure out some way to get this error to show up + // in test results, noting that the failure may occur in many + // different ways, including in loadModule() before the test + // runner even comes up. + goog.logToConsole_(path + suffix); + return code; + }; + } + // Note: any transpilation errors/warnings will be logged to the console. + return transpile(code, path, target); +}; + +//============================================================================== +// Language Enhancements +//============================================================================== + + +/** + * This is a "fixed" version of the typeof operator. It differs from the typeof + * operator in such a way that null returns 'null' and arrays return 'array'. + * @param {?} value The value to get the type of. + * @return {string} The name of the type. + */ +goog.typeOf = function(value) { + var s = typeof value; + if (s == 'object') { + if (value) { + // Check these first, so we can avoid calling Object.prototype.toString if + // possible. + // + // IE improperly marshals typeof across execution contexts, but a + // cross-context object will still return false for "instanceof Object". + if (value instanceof Array) { + return 'array'; + } else if (value instanceof Object) { + return s; + } + + // HACK: In order to use an Object prototype method on the arbitrary + // value, the compiler requires the value be cast to type Object, + // even though the ECMA spec explicitly allows it. + var className = Object.prototype.toString.call( + /** @type {!Object} */ (value)); + // In Firefox 3.6, attempting to access iframe window objects' length + // property throws an NS_ERROR_FAILURE, so we need to special-case it + // here. + if (className == '[object Window]') { + return 'object'; + } + + // We cannot always use constructor == Array or instanceof Array because + // different frames have different Array objects. In IE6, if the iframe + // where the array was created is destroyed, the array loses its + // prototype. Then dereferencing val.splice here throws an exception, so + // we can't use goog.isFunction. Calling typeof directly returns 'unknown' + // so that will work. In this case, this function will return false and + // most array functions will still work because the array is still + // array-like (supports length and []) even though it has lost its + // prototype. + // Mark Miller noticed that Object.prototype.toString + // allows access to the unforgeable [[Class]] property. + // 15.2.4.2 Object.prototype.toString ( ) + // When the toString method is called, the following steps are taken: + // 1. Get the [[Class]] property of this object. + // 2. Compute a string value by concatenating the three strings + // "[object ", Result(1), and "]". + // 3. Return Result(2). + // and this behavior survives the destruction of the execution context. + if ((className == '[object Array]' || + // In IE all non value types are wrapped as objects across window + // boundaries (not iframe though) so we have to do object detection + // for this edge case. + typeof value.length == 'number' && + typeof value.splice != 'undefined' && + typeof value.propertyIsEnumerable != 'undefined' && + !value.propertyIsEnumerable('splice') + + )) { + return 'array'; + } + // HACK: There is still an array case that fails. + // function ArrayImpostor() {} + // ArrayImpostor.prototype = []; + // var impostor = new ArrayImpostor; + // this can be fixed by getting rid of the fast path + // (value instanceof Array) and solely relying on + // (value && Object.prototype.toString.vall(value) === '[object Array]') + // but that would require many more function calls and is not warranted + // unless closure code is receiving objects from untrusted sources. + + // IE in cross-window calls does not correctly marshal the function type + // (it appears just as an object) so we cannot use just typeof val == + // 'function'. However, if the object has a call property, it is a + // function. + if ((className == '[object Function]' || + typeof value.call != 'undefined' && + typeof value.propertyIsEnumerable != 'undefined' && + !value.propertyIsEnumerable('call'))) { + return 'function'; + } + + } else { + return 'null'; + } + + } else if (s == 'function' && typeof value.call == 'undefined') { + // In Safari typeof nodeList returns 'function', and on Firefox typeof + // behaves similarly for HTML{Applet,Embed,Object}, Elements and RegExps. We + // would like to return object for those and we can detect an invalid + // function by making sure that the function object has a call method. + return 'object'; + } + return s; +}; + + +/** + * Returns true if the specified value is null. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is null. + */ +goog.isNull = function(val) { + return val === null; +}; + + +/** + * Returns true if the specified value is defined and not null. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is defined and not null. + */ +goog.isDefAndNotNull = function(val) { + // Note that undefined == null. + return val != null; +}; + + +/** + * Returns true if the specified value is an array. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is an array. + */ +goog.isArray = function(val) { + return goog.typeOf(val) == 'array'; +}; + + +/** + * Returns true if the object looks like an array. To qualify as array like + * the value needs to be either a NodeList or an object with a Number length + * property. Note that for this function neither strings nor functions are + * considered "array-like". + * + * @param {?} val Variable to test. + * @return {boolean} Whether variable is an array. + */ +goog.isArrayLike = function(val) { + var type = goog.typeOf(val); + // We do not use goog.isObject here in order to exclude function values. + return type == 'array' || type == 'object' && typeof val.length == 'number'; +}; + + +/** + * Returns true if the object looks like a Date. To qualify as Date-like the + * value needs to be an object and have a getFullYear() function. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is a like a Date. + */ +goog.isDateLike = function(val) { + return goog.isObject(val) && typeof val.getFullYear == 'function'; +}; + + +/** + * Returns true if the specified value is a function. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is a function. + */ +goog.isFunction = function(val) { + return goog.typeOf(val) == 'function'; +}; + + +/** + * Returns true if the specified value is an object. This includes arrays and + * functions. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is an object. + */ +goog.isObject = function(val) { + var type = typeof val; + return type == 'object' && val != null || type == 'function'; + // return Object(val) === val also works, but is slower, especially if val is + // not an object. +}; + + +/** + * Gets a unique ID for an object. This mutates the object so that further calls + * with the same object as a parameter returns the same value. The unique ID is + * guaranteed to be unique across the current session amongst objects that are + * passed into `getUid`. There is no guarantee that the ID is unique or + * consistent across sessions. It is unsafe to generate unique ID for function + * prototypes. + * + * @param {Object} obj The object to get the unique ID for. + * @return {number} The unique ID for the object. + */ +goog.getUid = function(obj) { + // TODO(arv): Make the type stricter, do not accept null. + + // In Opera window.hasOwnProperty exists but always returns false so we avoid + // using it. As a consequence the unique ID generated for BaseClass.prototype + // and SubClass.prototype will be the same. + return obj[goog.UID_PROPERTY_] || + (obj[goog.UID_PROPERTY_] = ++goog.uidCounter_); +}; + + +/** + * Whether the given object is already assigned a unique ID. + * + * This does not modify the object. + * + * @param {!Object} obj The object to check. + * @return {boolean} Whether there is an assigned unique id for the object. + */ +goog.hasUid = function(obj) { + return !!obj[goog.UID_PROPERTY_]; +}; + + +/** + * Removes the unique ID from an object. This is useful if the object was + * previously mutated using `goog.getUid` in which case the mutation is + * undone. + * @param {Object} obj The object to remove the unique ID field from. + */ +goog.removeUid = function(obj) { + // TODO(arv): Make the type stricter, do not accept null. + + // In IE, DOM nodes are not instances of Object and throw an exception if we + // try to delete. Instead we try to use removeAttribute. + if (obj !== null && 'removeAttribute' in obj) { + obj.removeAttribute(goog.UID_PROPERTY_); + } + + try { + delete obj[goog.UID_PROPERTY_]; + } catch (ex) { + } +}; + + +/** + * Name for unique ID property. Initialized in a way to help avoid collisions + * with other closure JavaScript on the same page. + * @type {string} + * @private + */ +goog.UID_PROPERTY_ = 'closure_uid_' + ((Math.random() * 1e9) >>> 0); + + +/** + * Counter for UID. + * @type {number} + * @private + */ +goog.uidCounter_ = 0; + + +/** + * Adds a hash code field to an object. The hash code is unique for the + * given object. + * @param {Object} obj The object to get the hash code for. + * @return {number} The hash code for the object. + * @deprecated Use goog.getUid instead. + */ +goog.getHashCode = goog.getUid; + + +/** + * Removes the hash code field from an object. + * @param {Object} obj The object to remove the field from. + * @deprecated Use goog.removeUid instead. + */ +goog.removeHashCode = goog.removeUid; + + +/** + * Clones a value. The input may be an Object, Array, or basic type. Objects and + * arrays will be cloned recursively. + * + * WARNINGS: + * goog.cloneObject does not detect reference loops. Objects that + * refer to themselves will cause infinite recursion. + * + * goog.cloneObject is unaware of unique identifiers, and copies + * UIDs created by getUid into cloned results. + * + * @param {*} obj The value to clone. + * @return {*} A clone of the input value. + * @deprecated goog.cloneObject is unsafe. Prefer the goog.object methods. + */ +goog.cloneObject = function(obj) { + var type = goog.typeOf(obj); + if (type == 'object' || type == 'array') { + if (typeof obj.clone === 'function') { + return obj.clone(); + } + var clone = type == 'array' ? [] : {}; + for (var key in obj) { + clone[key] = goog.cloneObject(obj[key]); + } + return clone; + } + + return obj; +}; + + +/** + * A native implementation of goog.bind. + * @param {?function(this:T, ...)} fn A function to partially apply. + * @param {T} selfObj Specifies the object which this should point to when the + * function is run. + * @param {...*} var_args Additional arguments that are partially applied to the + * function. + * @return {!Function} A partially-applied form of the function goog.bind() was + * invoked as a method of. + * @template T + * @private + */ +goog.bindNative_ = function(fn, selfObj, var_args) { + return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments)); +}; + + +/** + * A pure-JS implementation of goog.bind. + * @param {?function(this:T, ...)} fn A function to partially apply. + * @param {T} selfObj Specifies the object which this should point to when the + * function is run. + * @param {...*} var_args Additional arguments that are partially applied to the + * function. + * @return {!Function} A partially-applied form of the function goog.bind() was + * invoked as a method of. + * @template T + * @private + */ +goog.bindJs_ = function(fn, selfObj, var_args) { + if (!fn) { + throw new Error(); + } + + if (arguments.length > 2) { + var boundArgs = Array.prototype.slice.call(arguments, 2); + return function() { + // Prepend the bound arguments to the current arguments. + var newArgs = Array.prototype.slice.call(arguments); + Array.prototype.unshift.apply(newArgs, boundArgs); + return fn.apply(selfObj, newArgs); + }; + + } else { + return function() { + return fn.apply(selfObj, arguments); + }; + } +}; + + +/** + * Partially applies this function to a particular 'this object' and zero or + * more arguments. The result is a new function with some arguments of the first + * function pre-filled and the value of this 'pre-specified'. + * + * Remaining arguments specified at call-time are appended to the pre-specified + * ones. + * + * Also see: {@link #partial}. + * + * Usage: + *
var barMethBound = goog.bind(myFunction, myObj, 'arg1', 'arg2');
+ * barMethBound('arg3', 'arg4');
+ * + * @param {?function(this:T, ...)} fn A function to partially apply. + * @param {T} selfObj Specifies the object which this should point to when the + * function is run. + * @param {...*} var_args Additional arguments that are partially applied to the + * function. + * @return {!Function} A partially-applied form of the function goog.bind() was + * invoked as a method of. + * @template T + * @suppress {deprecated} See above. + */ +goog.bind = function(fn, selfObj, var_args) { + // TODO(nicksantos): narrow the type signature. + if (Function.prototype.bind && + // NOTE(nicksantos): Somebody pulled base.js into the default Chrome + // extension environment. This means that for Chrome extensions, they get + // the implementation of Function.prototype.bind that calls goog.bind + // instead of the native one. Even worse, we don't want to introduce a + // circular dependency between goog.bind and Function.prototype.bind, so + // we have to hack this to make sure it works correctly. + Function.prototype.bind.toString().indexOf('native code') != -1) { + goog.bind = goog.bindNative_; + } else { + goog.bind = goog.bindJs_; + } + return goog.bind.apply(null, arguments); +}; + + +/** + * Like goog.bind(), except that a 'this object' is not required. Useful when + * the target function is already bound. + * + * Usage: + * var g = goog.partial(f, arg1, arg2); + * g(arg3, arg4); + * + * @param {Function} fn A function to partially apply. + * @param {...*} var_args Additional arguments that are partially applied to fn. + * @return {!Function} A partially-applied form of the function goog.partial() + * was invoked as a method of. + */ +goog.partial = function(fn, var_args) { + var args = Array.prototype.slice.call(arguments, 1); + return function() { + // Clone the array (with slice()) and append additional arguments + // to the existing arguments. + var newArgs = args.slice(); + newArgs.push.apply(newArgs, arguments); + return fn.apply(/** @type {?} */ (this), newArgs); + }; +}; + + +/** + * Copies all the members of a source object to a target object. This method + * does not work on all browsers for all objects that contain keys such as + * toString or hasOwnProperty. Use goog.object.extend for this purpose. + * @param {Object} target Target. + * @param {Object} source Source. + */ +goog.mixin = function(target, source) { + for (var x in source) { + target[x] = source[x]; + } + + // For IE7 or lower, the for-in-loop does not contain any properties that are + // not enumerable on the prototype object (for example, isPrototypeOf from + // Object.prototype) but also it will not include 'replace' on objects that + // extend String and change 'replace' (not that it is common for anyone to + // extend anything except Object). +}; + + +/** + * @return {number} An integer value representing the number of milliseconds + * between midnight, January 1, 1970 and the current time. + */ +goog.now = (goog.TRUSTED_SITE && Date.now) || (function() { + // Unary plus operator converts its operand to a number which in + // the case of + // a date is done by calling getTime(). + return +new Date(); + }); + + +/** + * Evals JavaScript in the global scope. In IE this uses execScript, other + * browsers use goog.global.eval. If goog.global.eval does not evaluate in the + * global scope (for example, in Safari), appends a script tag instead. + * Throws an exception if neither execScript or eval is defined. + * @param {string} script JavaScript string. + */ +goog.globalEval = function(script) { + if (goog.global.execScript) { + goog.global.execScript(script, 'JavaScript'); + } else if (goog.global.eval) { + // Test to see if eval works + if (goog.evalWorksForGlobals_ == null) { + try { + goog.global.eval('var _evalTest_ = 1;'); + } catch (ignore) { + } + if (typeof goog.global['_evalTest_'] != 'undefined') { + try { + delete goog.global['_evalTest_']; + } catch (ignore) { + // Microsoft edge fails the deletion above in strict mode. + } + goog.evalWorksForGlobals_ = true; + } else { + goog.evalWorksForGlobals_ = false; + } + } + + if (goog.evalWorksForGlobals_) { + goog.global.eval(script); + } else { + /** @type {!Document} */ + var doc = goog.global.document; + var scriptElt = + /** @type {!HTMLScriptElement} */ (doc.createElement('SCRIPT')); + scriptElt.type = 'text/javascript'; + scriptElt.defer = false; + // Note(user): can't use .innerHTML since "t('')" will fail and + // .text doesn't work in Safari 2. Therefore we append a text node. + scriptElt.appendChild(doc.createTextNode(script)); + doc.head.appendChild(scriptElt); + doc.head.removeChild(scriptElt); + } + } else { + throw new Error('goog.globalEval not available'); + } +}; + + +/** + * Indicates whether or not we can call 'eval' directly to eval code in the + * global scope. Set to a Boolean by the first call to goog.globalEval (which + * empirically tests whether eval works for globals). @see goog.globalEval + * @type {?boolean} + * @private + */ +goog.evalWorksForGlobals_ = null; + + +/** + * Optional map of CSS class names to obfuscated names used with + * goog.getCssName(). + * @private {!Object|undefined} + * @see goog.setCssNameMapping + */ +goog.cssNameMapping_; + + +/** + * Optional obfuscation style for CSS class names. Should be set to either + * 'BY_WHOLE' or 'BY_PART' if defined. + * @type {string|undefined} + * @private + * @see goog.setCssNameMapping + */ +goog.cssNameMappingStyle_; + + + +/** + * A hook for modifying the default behavior goog.getCssName. The function + * if present, will receive the standard output of the goog.getCssName as + * its input. + * + * @type {(function(string):string)|undefined} + */ +goog.global.CLOSURE_CSS_NAME_MAP_FN; + + +/** + * Handles strings that are intended to be used as CSS class names. + * + * This function works in tandem with @see goog.setCssNameMapping. + * + * Without any mapping set, the arguments are simple joined with a hyphen and + * passed through unaltered. + * + * When there is a mapping, there are two possible styles in which these + * mappings are used. In the BY_PART style, each part (i.e. in between hyphens) + * of the passed in css name is rewritten according to the map. In the BY_WHOLE + * style, the full css name is looked up in the map directly. If a rewrite is + * not specified by the map, the compiler will output a warning. + * + * When the mapping is passed to the compiler, it will replace calls to + * goog.getCssName with the strings from the mapping, e.g. + * var x = goog.getCssName('foo'); + * var y = goog.getCssName(this.baseClass, 'active'); + * becomes: + * var x = 'foo'; + * var y = this.baseClass + '-active'; + * + * If one argument is passed it will be processed, if two are passed only the + * modifier will be processed, as it is assumed the first argument was generated + * as a result of calling goog.getCssName. + * + * @param {string} className The class name. + * @param {string=} opt_modifier A modifier to be appended to the class name. + * @return {string} The class name or the concatenation of the class name and + * the modifier. + */ +goog.getCssName = function(className, opt_modifier) { + // String() is used for compatibility with compiled soy where the passed + // className can be non-string objects. + if (String(className).charAt(0) == '.') { + throw new Error( + 'className passed in goog.getCssName must not start with ".".' + + ' You passed: ' + className); + } + + var getMapping = function(cssName) { + return goog.cssNameMapping_[cssName] || cssName; + }; + + var renameByParts = function(cssName) { + // Remap all the parts individually. + var parts = cssName.split('-'); + var mapped = []; + for (var i = 0; i < parts.length; i++) { + mapped.push(getMapping(parts[i])); + } + return mapped.join('-'); + }; + + var rename; + if (goog.cssNameMapping_) { + rename = + goog.cssNameMappingStyle_ == 'BY_WHOLE' ? getMapping : renameByParts; + } else { + rename = function(a) { + return a; + }; + } + + var result = + opt_modifier ? className + '-' + rename(opt_modifier) : rename(className); + + // The special CLOSURE_CSS_NAME_MAP_FN allows users to specify further + // processing of the class name. + if (goog.global.CLOSURE_CSS_NAME_MAP_FN) { + return goog.global.CLOSURE_CSS_NAME_MAP_FN(result); + } + + return result; +}; + + +/** + * Sets the map to check when returning a value from goog.getCssName(). Example: + *
+ * goog.setCssNameMapping({
+ *   "goog": "a",
+ *   "disabled": "b",
+ * });
+ *
+ * var x = goog.getCssName('goog');
+ * // The following evaluates to: "a a-b".
+ * goog.getCssName('goog') + ' ' + goog.getCssName(x, 'disabled')
+ * 
+ * When declared as a map of string literals to string literals, the JSCompiler + * will replace all calls to goog.getCssName() using the supplied map if the + * --process_closure_primitives flag is set. + * + * @param {!Object} mapping A map of strings to strings where keys are possible + * arguments to goog.getCssName() and values are the corresponding values + * that should be returned. + * @param {string=} opt_style The style of css name mapping. There are two valid + * options: 'BY_PART', and 'BY_WHOLE'. + * @see goog.getCssName for a description. + */ +goog.setCssNameMapping = function(mapping, opt_style) { + goog.cssNameMapping_ = mapping; + goog.cssNameMappingStyle_ = opt_style; +}; + + +/** + * To use CSS renaming in compiled mode, one of the input files should have a + * call to goog.setCssNameMapping() with an object literal that the JSCompiler + * can extract and use to replace all calls to goog.getCssName(). In uncompiled + * mode, JavaScript code should be loaded before this base.js file that declares + * a global variable, CLOSURE_CSS_NAME_MAPPING, which is used below. This is + * to ensure that the mapping is loaded before any calls to goog.getCssName() + * are made in uncompiled mode. + * + * A hook for overriding the CSS name mapping. + * @type {!Object|undefined} + */ +goog.global.CLOSURE_CSS_NAME_MAPPING; + + +if (!COMPILED && goog.global.CLOSURE_CSS_NAME_MAPPING) { + // This does not call goog.setCssNameMapping() because the JSCompiler + // requires that goog.setCssNameMapping() be called with an object literal. + goog.cssNameMapping_ = goog.global.CLOSURE_CSS_NAME_MAPPING; +} + + +/** + * Gets a localized message. + * + * This function is a compiler primitive. If you give the compiler a localized + * message bundle, it will replace the string at compile-time with a localized + * version, and expand goog.getMsg call to a concatenated string. + * + * Messages must be initialized in the form: + * + * var MSG_NAME = goog.getMsg('Hello {$placeholder}', {'placeholder': 'world'}); + * + * + * This function produces a string which should be treated as plain text. Use + * {@link goog.html.SafeHtmlFormatter} in conjunction with goog.getMsg to + * produce SafeHtml. + * + * @param {string} str Translatable string, places holders in the form {$foo}. + * @param {Object=} opt_values Maps place holder name to value. + * @return {string} message with placeholders filled. + */ +goog.getMsg = function(str, opt_values) { + if (opt_values) { + str = str.replace(/\{\$([^}]+)}/g, function(match, key) { + return (opt_values != null && key in opt_values) ? opt_values[key] : + match; + }); + } + return str; +}; + + +/** + * Gets a localized message. If the message does not have a translation, gives a + * fallback message. + * + * This is useful when introducing a new message that has not yet been + * translated into all languages. + * + * This function is a compiler primitive. Must be used in the form: + * var x = goog.getMsgWithFallback(MSG_A, MSG_B); + * where MSG_A and MSG_B were initialized with goog.getMsg. + * + * @param {string} a The preferred message. + * @param {string} b The fallback message. + * @return {string} The best translated message. + */ +goog.getMsgWithFallback = function(a, b) { + return a; +}; + + +/** + * Exposes an unobfuscated global namespace path for the given object. + * Note that fields of the exported object *will* be obfuscated, unless they are + * exported in turn via this function or goog.exportProperty. + * + * Also handy for making public items that are defined in anonymous closures. + * + * ex. goog.exportSymbol('public.path.Foo', Foo); + * + * ex. goog.exportSymbol('public.path.Foo.staticFunction', Foo.staticFunction); + * public.path.Foo.staticFunction(); + * + * ex. goog.exportSymbol('public.path.Foo.prototype.myMethod', + * Foo.prototype.myMethod); + * new public.path.Foo().myMethod(); + * + * @param {string} publicPath Unobfuscated name to export. + * @param {*} object Object the name should point to. + * @param {Object=} opt_objectToExportTo The object to add the path to; default + * is goog.global. + */ +goog.exportSymbol = function(publicPath, object, opt_objectToExportTo) { + goog.exportPath_(publicPath, object, opt_objectToExportTo); +}; + + +/** + * Exports a property unobfuscated into the object's namespace. + * ex. goog.exportProperty(Foo, 'staticFunction', Foo.staticFunction); + * ex. goog.exportProperty(Foo.prototype, 'myMethod', Foo.prototype.myMethod); + * @param {Object} object Object whose static property is being exported. + * @param {string} publicName Unobfuscated name to export. + * @param {*} symbol Object the name should point to. + */ +goog.exportProperty = function(object, publicName, symbol) { + object[publicName] = symbol; +}; + + +/** + * Inherit the prototype methods from one constructor into another. + * + * Usage: + *
+ * function ParentClass(a, b) { }
+ * ParentClass.prototype.foo = function(a) { };
+ *
+ * function ChildClass(a, b, c) {
+ *   ChildClass.base(this, 'constructor', a, b);
+ * }
+ * goog.inherits(ChildClass, ParentClass);
+ *
+ * var child = new ChildClass('a', 'b', 'see');
+ * child.foo(); // This works.
+ * 
+ * + * @param {!Function} childCtor Child class. + * @param {!Function} parentCtor Parent class. + * @suppress {strictMissingProperties} superClass_ and base is not defined on + * Function. + */ +goog.inherits = function(childCtor, parentCtor) { + /** @constructor */ + function tempCtor() {} + tempCtor.prototype = parentCtor.prototype; + childCtor.superClass_ = parentCtor.prototype; + childCtor.prototype = new tempCtor(); + /** @override */ + childCtor.prototype.constructor = childCtor; + + /** + * Calls superclass constructor/method. + * + * This function is only available if you use goog.inherits to + * express inheritance relationships between classes. + * + * NOTE: This is a replacement for goog.base and for superClass_ + * property defined in childCtor. + * + * @param {!Object} me Should always be "this". + * @param {string} methodName The method name to call. Calling + * superclass constructor can be done with the special string + * 'constructor'. + * @param {...*} var_args The arguments to pass to superclass + * method/constructor. + * @return {*} The return value of the superclass method/constructor. + */ + childCtor.base = function(me, methodName, var_args) { + // Copying using loop to avoid deop due to passing arguments object to + // function. This is faster in many JS engines as of late 2014. + var args = new Array(arguments.length - 2); + for (var i = 2; i < arguments.length; i++) { + args[i - 2] = arguments[i]; + } + return parentCtor.prototype[methodName].apply(me, args); + }; +}; + + +/** + * Call up to the superclass. + * + * If this is called from a constructor, then this calls the superclass + * constructor with arguments 1-N. + * + * If this is called from a prototype method, then you must pass the name of the + * method as the second argument to this function. If you do not, you will get a + * runtime error. This calls the superclass' method with arguments 2-N. + * + * This function only works if you use goog.inherits to express inheritance + * relationships between your classes. + * + * This function is a compiler primitive. At compile-time, the compiler will do + * macro expansion to remove a lot of the extra overhead that this function + * introduces. The compiler will also enforce a lot of the assumptions that this + * function makes, and treat it as a compiler error if you break them. + * + * @param {!Object} me Should always be "this". + * @param {*=} opt_methodName The method name if calling a super method. + * @param {...*} var_args The rest of the arguments. + * @return {*} The return value of the superclass method. + * @suppress {es5Strict} This method can not be used in strict mode, but + * all Closure Library consumers must depend on this file. + * @deprecated goog.base is not strict mode compatible. Prefer the static + * "base" method added to the constructor by goog.inherits + * or ES6 classes and the "super" keyword. + */ +goog.base = function(me, opt_methodName, var_args) { + var caller = arguments.callee.caller; + + if (goog.STRICT_MODE_COMPATIBLE || (goog.DEBUG && !caller)) { + throw new Error( + 'arguments.caller not defined. goog.base() cannot be used ' + + 'with strict mode code. See ' + + 'http://www.ecma-international.org/ecma-262/5.1/#sec-C'); + } + + if (typeof caller.superClass_ !== 'undefined') { + // Copying using loop to avoid deop due to passing arguments object to + // function. This is faster in many JS engines as of late 2014. + var ctorArgs = new Array(arguments.length - 1); + for (var i = 1; i < arguments.length; i++) { + ctorArgs[i - 1] = arguments[i]; + } + // This is a constructor. Call the superclass constructor. + return /** @type {!Function} */ (caller.superClass_) + .constructor.apply(me, ctorArgs); + } + + if (typeof opt_methodName != 'string' && typeof opt_methodName != 'symbol') { + throw new Error( + 'method names provided to goog.base must be a string or a symbol'); + } + + // Copying using loop to avoid deop due to passing arguments object to + // function. This is faster in many JS engines as of late 2014. + var args = new Array(arguments.length - 2); + for (var i = 2; i < arguments.length; i++) { + args[i - 2] = arguments[i]; + } + var foundCaller = false; + for (var ctor = me.constructor; ctor; + ctor = ctor.superClass_ && ctor.superClass_.constructor) { + if (ctor.prototype[opt_methodName] === caller) { + foundCaller = true; + } else if (foundCaller) { + return ctor.prototype[opt_methodName].apply(me, args); + } + } + + // If we did not find the caller in the prototype chain, then one of two + // things happened: + // 1) The caller is an instance method. + // 2) This method was not called by the right caller. + if (me[opt_methodName] === caller) { + return me.constructor.prototype[opt_methodName].apply(me, args); + } else { + throw new Error( + 'goog.base called from a method of one name ' + + 'to a method of a different name'); + } +}; + + +/** + * Allow for aliasing within scope functions. This function exists for + * uncompiled code - in compiled code the calls will be inlined and the aliases + * applied. In uncompiled code the function is simply run since the aliases as + * written are valid JavaScript. + * + * + * @param {function()} fn Function to call. This function can contain aliases + * to namespaces (e.g. "var dom = goog.dom") or classes + * (e.g. "var Timer = goog.Timer"). + */ +goog.scope = function(fn) { + if (goog.isInModuleLoader_()) { + throw new Error('goog.scope is not supported within a module.'); + } + fn.call(goog.global); +}; + + +/* + * To support uncompiled, strict mode bundles that use eval to divide source + * like so: + * eval('someSource;//# sourceUrl sourcefile.js'); + * We need to export the globally defined symbols "goog" and "COMPILED". + * Exporting "goog" breaks the compiler optimizations, so we required that + * be defined externally. + * NOTE: We don't use goog.exportSymbol here because we don't want to trigger + * extern generation when that compiler option is enabled. + */ +if (!COMPILED) { + goog.global['COMPILED'] = COMPILED; +} + + +//============================================================================== +// goog.defineClass implementation +//============================================================================== + + +/** + * Creates a restricted form of a Closure "class": + * - from the compiler's perspective, the instance returned from the + * constructor is sealed (no new properties may be added). This enables + * better checks. + * - the compiler will rewrite this definition to a form that is optimal + * for type checking and optimization (initially this will be a more + * traditional form). + * + * @param {Function} superClass The superclass, Object or null. + * @param {goog.defineClass.ClassDescriptor} def + * An object literal describing + * the class. It may have the following properties: + * "constructor": the constructor function + * "statics": an object literal containing methods to add to the constructor + * as "static" methods or a function that will receive the constructor + * function as its only parameter to which static properties can + * be added. + * all other properties are added to the prototype. + * @return {!Function} The class constructor. + */ +goog.defineClass = function(superClass, def) { + // TODO(johnlenz): consider making the superClass an optional parameter. + var constructor = def.constructor; + var statics = def.statics; + // Wrap the constructor prior to setting up the prototype and static methods. + if (!constructor || constructor == Object.prototype.constructor) { + constructor = function() { + throw new Error( + 'cannot instantiate an interface (no constructor defined).'); + }; + } + + var cls = goog.defineClass.createSealingConstructor_(constructor, superClass); + if (superClass) { + goog.inherits(cls, superClass); + } + + // Remove all the properties that should not be copied to the prototype. + delete def.constructor; + delete def.statics; + + goog.defineClass.applyProperties_(cls.prototype, def); + if (statics != null) { + if (statics instanceof Function) { + statics(cls); + } else { + goog.defineClass.applyProperties_(cls, statics); + } + } + + return cls; +}; + + +/** + * @typedef {{ + * constructor: (!Function|undefined), + * statics: (Object|undefined|function(Function):void) + * }} + */ +goog.defineClass.ClassDescriptor; + + +/** + * @define {boolean} Whether the instances returned by goog.defineClass should + * be sealed when possible. + * + * When sealing is disabled the constructor function will not be wrapped by + * goog.defineClass, making it incompatible with ES6 class methods. + */ +goog.define('goog.defineClass.SEAL_CLASS_INSTANCES', goog.DEBUG); + + +/** + * If goog.defineClass.SEAL_CLASS_INSTANCES is enabled and Object.seal is + * defined, this function will wrap the constructor in a function that seals the + * results of the provided constructor function. + * + * @param {!Function} ctr The constructor whose results maybe be sealed. + * @param {Function} superClass The superclass constructor. + * @return {!Function} The replacement constructor. + * @private + */ +goog.defineClass.createSealingConstructor_ = function(ctr, superClass) { + if (!goog.defineClass.SEAL_CLASS_INSTANCES) { + // Do now wrap the constructor when sealing is disabled. Angular code + // depends on this for injection to work properly. + return ctr; + } + + // Compute whether the constructor is sealable at definition time, rather + // than when the instance is being constructed. + var superclassSealable = !goog.defineClass.isUnsealable_(superClass); + + /** + * @this {Object} + * @return {?} + */ + var wrappedCtr = function() { + // Don't seal an instance of a subclass when it calls the constructor of + // its super class as there is most likely still setup to do. + var instance = ctr.apply(this, arguments) || this; + instance[goog.UID_PROPERTY_] = instance[goog.UID_PROPERTY_]; + + if (this.constructor === wrappedCtr && superclassSealable && + Object.seal instanceof Function) { + Object.seal(instance); + } + return instance; + }; + + return wrappedCtr; +}; + + +/** + * @param {Function} ctr The constructor to test. + * @return {boolean} Whether the constructor has been tagged as unsealable + * using goog.tagUnsealableClass. + * @private + */ +goog.defineClass.isUnsealable_ = function(ctr) { + return ctr && ctr.prototype && + ctr.prototype[goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_]; +}; + + +// TODO(johnlenz): share these values with the goog.object +/** + * The names of the fields that are defined on Object.prototype. + * @type {!Array} + * @private + * @const + */ +goog.defineClass.OBJECT_PROTOTYPE_FIELDS_ = [ + 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', + 'toLocaleString', 'toString', 'valueOf' +]; + + +// TODO(johnlenz): share this function with the goog.object +/** + * @param {!Object} target The object to add properties to. + * @param {!Object} source The object to copy properties from. + * @private + */ +goog.defineClass.applyProperties_ = function(target, source) { + // TODO(johnlenz): update this to support ES5 getters/setters + + var key; + for (key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + + // For IE the for-in-loop does not contain any properties that are not + // enumerable on the prototype object (for example isPrototypeOf from + // Object.prototype) and it will also not include 'replace' on objects that + // extend String and change 'replace' (not that it is common for anyone to + // extend anything except Object). + for (var i = 0; i < goog.defineClass.OBJECT_PROTOTYPE_FIELDS_.length; i++) { + key = goog.defineClass.OBJECT_PROTOTYPE_FIELDS_[i]; + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } +}; + + +/** + * Sealing classes breaks the older idiom of assigning properties on the + * prototype rather than in the constructor. As such, goog.defineClass + * must not seal subclasses of these old-style classes until they are fixed. + * Until then, this marks a class as "broken", instructing defineClass + * not to seal subclasses. + * @param {!Function} ctr The legacy constructor to tag as unsealable. + */ +goog.tagUnsealableClass = function(ctr) { + if (!COMPILED && goog.defineClass.SEAL_CLASS_INSTANCES) { + ctr.prototype[goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_] = true; + } +}; + + +/** + * Name for unsealable tag property. + * @const @private {string} + */ +goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_ = 'goog_defineClass_legacy_unsealable'; + + +// There's a bug in the compiler where without collapse properties the +// Closure namespace defines do not guard code correctly. To help reduce code +// size also check for !COMPILED even though it redundant until this is fixed. +if (!COMPILED && goog.DEPENDENCIES_ENABLED) { + + /** + * Tries to detect whether is in the context of an HTML document. + * @return {boolean} True if it looks like HTML document. + * @private + */ + goog.inHtmlDocument_ = function() { + /** @type {!Document} */ + var doc = goog.global.document; + return doc != null && 'write' in doc; // XULDocument misses write. + }; + + + /** + * We'd like to check for if the document readyState is 'loading'; however + * there are bugs on IE 10 and below where the readyState being anything other + * than 'complete' is not reliable. + * @return {boolean} + * @private + */ + goog.isDocumentLoading_ = function() { + // attachEvent is available on IE 6 thru 10 only, and thus can be used to + // detect those browsers. + /** @type {!HTMLDocument} */ + var doc = goog.global.document; + return doc.attachEvent ? doc.readyState != 'complete' : + doc.readyState == 'loading'; + }; + + + /** + * Tries to detect the base path of base.js script that bootstraps Closure. + * @private + */ + goog.findBasePath_ = function() { + if (goog.isDef(goog.global.CLOSURE_BASE_PATH) && + // Anti DOM-clobbering runtime check (b/37736576). + goog.isString(goog.global.CLOSURE_BASE_PATH)) { + goog.basePath = goog.global.CLOSURE_BASE_PATH; + return; + } else if (!goog.inHtmlDocument_()) { + return; + } + /** @type {!Document} */ + var doc = goog.global.document; + // If we have a currentScript available, use it exclusively. + var currentScript = doc.currentScript; + if (currentScript) { + var scripts = [currentScript]; + } else { + 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 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') { + goog.basePath = src.substr(0, l - 7); + return; + } + } + }; + + goog.findBasePath_(); + + /** @struct @constructor @final */ + goog.Transpiler = function() { + /** @private {?Object} */ + this.requiresTranspilation_ = null; + /** @private {string} */ + this.transpilationTarget_ = goog.TRANSPILE_TO_LANGUAGE; + }; + + + /** + * Returns a newly created map from language mode string to a boolean + * indicating whether transpilation should be done for that mode as well as + * the highest level language that this environment supports. + * + * Guaranteed invariant: + * For any two modes, l1 and l2 where l2 is a newer mode than l1, + * `map[l1] == true` implies that `map[l2] == true`. + * + * Note this method is extracted and used elsewhere, so it cannot rely on + * anything external (it should easily be able to be transformed into a + * standalone, top level function). + * + * @private + * @return {{ + * target: string, + * map: !Object + * }} + */ + goog.Transpiler.prototype.createRequiresTranspilation_ = function() { + var transpilationTarget = 'es3'; + var /** !Object */ requiresTranspilation = {'es3': false}; + var transpilationRequiredForAllLaterModes = false; + + /** + * Adds an entry to requiresTranspliation for the given language mode. + * + * IMPORTANT: Calls must be made in order from oldest to newest language + * mode. + * @param {string} modeName + * @param {function(): boolean} isSupported Returns true if the JS engine + * supports the given mode. + */ + function addNewerLanguageTranspilationCheck(modeName, isSupported) { + if (transpilationRequiredForAllLaterModes) { + requiresTranspilation[modeName] = true; + } else if (isSupported()) { + transpilationTarget = modeName; + requiresTranspilation[modeName] = false; + } else { + requiresTranspilation[modeName] = true; + transpilationRequiredForAllLaterModes = true; + } + } + + /** + * Does the given code evaluate without syntax errors and return a truthy + * result? + */ + function /** boolean */ evalCheck(/** string */ code) { + try { + return !!eval(code); + } catch (ignored) { + return false; + } + } + + var userAgent = goog.global.navigator && goog.global.navigator.userAgent ? + goog.global.navigator.userAgent : + ''; + + // Identify ES3-only browsers by their incorrect treatment of commas. + addNewerLanguageTranspilationCheck('es5', function() { + return evalCheck('[1,].length==1'); + }); + addNewerLanguageTranspilationCheck('es6', function() { + // Edge has a non-deterministic (i.e., not reproducible) bug with ES6: + // https://github.com/Microsoft/ChakraCore/issues/1496. + var re = /Edge\/(\d+)(\.\d)*/i; + var edgeUserAgent = userAgent.match(re); + if (edgeUserAgent) { + // The Reflect.construct test below is flaky on Edge. It can sometimes + // pass or fail on 40 15.15063, so just exit early for Edge and treat + // it as ES5. Until we're on a more up to date version just always use + // ES5. See https://github.com/Microsoft/ChakraCore/issues/3217. + return false; + } + // Test es6: [FF50 (?), Edge 14 (?), Chrome 50] + // (a) default params (specifically shadowing locals), + // (b) destructuring, (c) block-scoped functions, + // (d) for-of (const), (e) new.target/Reflect.construct + var es6fullTest = + 'class X{constructor(){if(new.target!=String)throw 1;this.x=42}}' + + 'let q=Reflect.construct(X,[],String);if(q.x!=42||!(q instanceof ' + + 'String))throw 1;for(const a of[2,3]){if(a==2)continue;function ' + + 'f(z={a}){let a=0;return z.a}{function f(){return 0;}}return f()' + + '==3}'; + + return evalCheck('(()=>{"use strict";' + es6fullTest + '})()'); + }); + // TODO(joeltine): Remove es6-impl references for b/31340605. + // Consider es6-impl (widely-implemented es6 features) to be supported + // whenever es6 is supported. Technically es6-impl is a lower level of + // support than es6, but we don't have tests specifically for it. + addNewerLanguageTranspilationCheck('es6-impl', function() { + return true; + }); + // ** and **= are the only new features in 'es7' + addNewerLanguageTranspilationCheck('es7', function() { + return evalCheck('2 ** 2 == 4'); + }); + // async functions are the only new features in 'es8' + addNewerLanguageTranspilationCheck('es8', function() { + return evalCheck('async () => 1, true'); + }); + addNewerLanguageTranspilationCheck('es9', function() { + return evalCheck('({...rest} = {}), true'); + }); + addNewerLanguageTranspilationCheck('es_next', function() { + return false; // assume it always need to transpile + }); + return {target: transpilationTarget, map: requiresTranspilation}; + }; + + + /** + * Determines whether the given language needs to be transpiled. + * @param {string} lang + * @param {string|undefined} module + * @return {boolean} + */ + goog.Transpiler.prototype.needsTranspile = function(lang, module) { + if (goog.TRANSPILE == 'always') { + return true; + } else if (goog.TRANSPILE == 'never') { + return false; + } else if (!this.requiresTranspilation_) { + var obj = this.createRequiresTranspilation_(); + this.requiresTranspilation_ = obj.map; + this.transpilationTarget_ = this.transpilationTarget_ || obj.target; + } + if (lang in this.requiresTranspilation_) { + if (this.requiresTranspilation_[lang]) { + return true; + } else if ( + goog.inHtmlDocument_() && module == 'es6' && + !('noModule' in goog.global.document.createElement('script'))) { + return true; + } else { + return false; + } + } else { + throw new Error('Unknown language mode: ' + lang); + } + }; + + + /** + * Lazily retrieves the transpiler and applies it to the source. + * @param {string} code JS code. + * @param {string} path Path to the code. + * @return {string} The transpiled code. + */ + goog.Transpiler.prototype.transpile = function(code, path) { + // TODO(johnplaisted): We should delete goog.transpile_ and just have this + // function. But there's some compile error atm where goog.global is being + // stripped incorrectly without this. + return goog.transpile_(code, path, this.transpilationTarget_); + }; + + + /** @private @final {!goog.Transpiler} */ + goog.transpiler_ = new goog.Transpiler(); + + /** + * Rewrites closing script tags in input to avoid ending an enclosing script + * tag. + * + * @param {string} str + * @return {string} + * @private + */ + goog.protectScriptTag_ = function(str) { + return str.replace(/<\/(SCRIPT)/ig, '\\x3c/$1'); + }; + + + /** + * A debug loader is responsible for downloading and executing javascript + * files in an unbundled, uncompiled environment. + * + * This can be custimized via the setDependencyFactory method, or by + * CLOSURE_IMPORT_SCRIPT/CLOSURE_LOAD_FILE_SYNC. + * + * @struct @constructor @final @private + */ + goog.DebugLoader_ = function() { + /** @private @const {!Object} */ + this.dependencies_ = {}; + /** @private @const {!Object} */ + this.idToPath_ = {}; + /** @private @const {!Object} */ + this.written_ = {}; + /** @private @const {!Array} */ + this.loadingDeps_ = []; + /** @private {!Array} */ + this.depsToLoad_ = []; + /** @private {boolean} */ + this.paused_ = false; + /** @private {!goog.DependencyFactory} */ + this.factory_ = new goog.DependencyFactory(goog.transpiler_); + /** @private @const {!Object} */ + this.deferredCallbacks_ = {}; + /** @private @const {!Array} */ + this.deferredQueue_ = []; + }; + + /** + * @param {!Array} namespaces + * @param {function(): undefined} callback Function to call once all the + * namespaces have loaded. + */ + goog.DebugLoader_.prototype.bootstrap = function(namespaces, callback) { + var cb = callback; + function resolve() { + if (cb) { + goog.global.setTimeout(cb, 0); + cb = null; + } + } + + if (!namespaces.length) { + resolve(); + return; + } + + var deps = []; + for (var i = 0; i < namespaces.length; i++) { + var path = this.getPathFromDeps_(namespaces[i]); + if (!path) { + throw new Error('Unregonized namespace: ' + namespaces[i]); + } + deps.push(this.dependencies_[path]); + } + + var require = goog.require; + var loaded = 0; + for (var i = 0; i < namespaces.length; i++) { + require(namespaces[i]); + deps[i].onLoad(function() { + if (++loaded == namespaces.length) { + resolve(); + } + }); + } + }; + + + /** + * Loads the Closure Dependency file. + * + * Exposed a public function so CLOSURE_NO_DEPS can be set to false, base + * loaded, setDependencyFactory called, and then this called. i.e. allows + * custom loading of the deps file. + */ + goog.DebugLoader_.prototype.loadClosureDeps = function() { + // Circumvent addDependency, which would try to transpile deps.js if + // transpile is set to always. + var relPath = 'deps.js'; + this.depsToLoad_.push(this.factory_.createDependency( + goog.normalizePath_(goog.basePath + relPath), relPath, [], [], {}, + false)); + this.loadDeps_(); + }; + + + /** + * Notifies the debug loader when a dependency has been requested. + * + * @param {string} absPathOrId Path of the dependency or goog id. + * @param {boolean=} opt_force + */ + goog.DebugLoader_.prototype.requested = function(absPathOrId, opt_force) { + var path = this.getPathFromDeps_(absPathOrId); + if (path && + (opt_force || this.areDepsLoaded_(this.dependencies_[path].requires))) { + var callback = this.deferredCallbacks_[path]; + if (callback) { + delete this.deferredCallbacks_[path]; + callback(); + } + } + }; + + + /** + * Sets the dependency factory, which can be used to create custom + * goog.Dependency implementations to control how dependencies are loaded. + * + * @param {!goog.DependencyFactory} factory + */ + goog.DebugLoader_.prototype.setDependencyFactory = function(factory) { + this.factory_ = factory; + }; + + + /** + * Travserses the dependency graph and queues the given dependency, and all of + * its transitive dependencies, for loading and then starts loading if not + * paused. + * + * @param {string} namespace + * @private + */ + goog.DebugLoader_.prototype.load_ = function(namespace) { + if (!this.getPathFromDeps_(namespace)) { + var errorMessage = 'goog.require could not find: ' + namespace; + + goog.logToConsole_(errorMessage); + throw Error(errorMessage); + } else { + var loader = this; + + var deps = []; + + /** @param {string} namespace */ + var visit = function(namespace) { + var path = loader.getPathFromDeps_(namespace); + + if (!path) { + throw new Error('Bad dependency path or symbol: ' + namespace); + } + + if (loader.written_[path]) { + return; + } + + loader.written_[path] = true; + + var dep = loader.dependencies_[path]; + for (var i = 0; i < dep.requires.length; i++) { + if (!goog.isProvided_(dep.requires[i])) { + visit(dep.requires[i]); + } + } + + deps.push(dep); + }; + + visit(namespace); + + var wasLoading = !!this.depsToLoad_.length; + this.depsToLoad_ = this.depsToLoad_.concat(deps); + + if (!this.paused_ && !wasLoading) { + this.loadDeps_(); + } + } + }; + + + /** + * Loads any queued dependencies until they are all loaded or paused. + * + * @private + */ + goog.DebugLoader_.prototype.loadDeps_ = function() { + var loader = this; + var paused = this.paused_; + + while (this.depsToLoad_.length && !paused) { + (function() { + var loadCallDone = false; + var dep = loader.depsToLoad_.shift(); + + var loaded = false; + loader.loading_(dep); + + var controller = { + pause: function() { + if (loadCallDone) { + throw new Error('Cannot call pause after the call to load.'); + } else { + paused = true; + } + }, + resume: function() { + if (loadCallDone) { + loader.resume_(); + } else { + // Some dep called pause and then resume in the same load call. + // Just keep running this same loop. + paused = false; + } + }, + loaded: function() { + if (loaded) { + throw new Error('Double call to loaded.'); + } + + loaded = true; + loader.loaded_(dep); + }, + pending: function() { + // Defensive copy. + var pending = []; + for (var i = 0; i < loader.loadingDeps_.length; i++) { + pending.push(loader.loadingDeps_[i]); + } + return pending; + }, + /** + * @param {goog.ModuleType} type + */ + setModuleState: function(type) { + goog.moduleLoaderState_ = { + type: type, + moduleName: '', + declareLegacyNamespace: false + }; + }, + /** @type {function(string, string, string=)} */ + registerEs6ModuleExports: function( + path, exports, opt_closureNamespace) { + if (opt_closureNamespace) { + goog.loadedModules_[opt_closureNamespace] = { + exports: exports, + type: goog.ModuleType.ES6, + moduleId: opt_closureNamespace || '' + }; + } + }, + /** @type {function(string, ?)} */ + registerGoogModuleExports: function(moduleId, exports) { + goog.loadedModules_[moduleId] = { + exports: exports, + type: goog.ModuleType.GOOG, + moduleId: moduleId + }; + }, + clearModuleState: function() { + goog.moduleLoaderState_ = null; + }, + defer: function(callback) { + if (loadCallDone) { + throw new Error( + 'Cannot register with defer after the call to load.'); + } + loader.defer_(dep, callback); + }, + areDepsLoaded: function() { + return loader.areDepsLoaded_(dep.requires); + } + }; + + try { + dep.load(controller); + } finally { + loadCallDone = true; + } + })(); + } + + if (paused) { + this.pause_(); + } + }; + + + /** @private */ + goog.DebugLoader_.prototype.pause_ = function() { + this.paused_ = true; + }; + + + /** @private */ + goog.DebugLoader_.prototype.resume_ = function() { + if (this.paused_) { + this.paused_ = false; + this.loadDeps_(); + } + }; + + + /** + * Marks the given dependency as loading (load has been called but it has not + * yet marked itself as finished). Useful for dependencies that want to know + * what else is loading. Example: goog.modules cannot eval if there are + * loading dependencies. + * + * @param {!goog.Dependency} dep + * @private + */ + goog.DebugLoader_.prototype.loading_ = function(dep) { + this.loadingDeps_.push(dep); + }; + + + /** + * Marks the given dependency as having finished loading and being available + * for require. + * + * @param {!goog.Dependency} dep + * @private + */ + goog.DebugLoader_.prototype.loaded_ = function(dep) { + for (var i = 0; i < this.loadingDeps_.length; i++) { + if (this.loadingDeps_[i] == dep) { + this.loadingDeps_.splice(i, 1); + break; + } + } + + for (var i = 0; i < this.deferredQueue_.length; i++) { + if (this.deferredQueue_[i] == dep.path) { + this.deferredQueue_.splice(i, 1); + break; + } + } + + if (this.loadingDeps_.length == this.deferredQueue_.length && + !this.depsToLoad_.length) { + // Something has asked to load these, but they may not be directly + // required again later, so load them now that we know we're done loading + // everything else. e.g. a goog module entry point. + while (this.deferredQueue_.length) { + this.requested(this.deferredQueue_.shift(), true); + } + } + + dep.loaded(); + }; + + + /** + * @param {!Array} pathsOrIds + * @return {boolean} + * @private + */ + goog.DebugLoader_.prototype.areDepsLoaded_ = function(pathsOrIds) { + for (var i = 0; i < pathsOrIds.length; i++) { + var path = this.getPathFromDeps_(pathsOrIds[i]); + if (!path || + (!(path in this.deferredCallbacks_) && + !goog.isProvided_(pathsOrIds[i]))) { + return false; + } + } + + return true; + }; + + + /** + * @param {string} absPathOrId + * @return {?string} + * @private + */ + goog.DebugLoader_.prototype.getPathFromDeps_ = function(absPathOrId) { + if (absPathOrId in this.idToPath_) { + return this.idToPath_[absPathOrId]; + } else if (absPathOrId in this.dependencies_) { + return absPathOrId; + } else { + return null; + } + }; + + + /** + * @param {!goog.Dependency} dependency + * @param {!Function} callback + * @private + */ + goog.DebugLoader_.prototype.defer_ = function(dependency, callback) { + this.deferredCallbacks_[dependency.path] = callback; + this.deferredQueue_.push(dependency.path); + }; + + + /** + * Interface for goog.Dependency implementations to have some control over + * loading of dependencies. + * + * @record + */ + goog.LoadController = function() {}; + + + /** + * Tells the controller to halt loading of more dependencies. + */ + goog.LoadController.prototype.pause = function() {}; + + + /** + * Tells the controller to resume loading of more dependencies if paused. + */ + goog.LoadController.prototype.resume = function() {}; + + + /** + * Tells the controller that this dependency has finished loading. + * + * This causes this to be removed from pending() and any load callbacks to + * fire. + */ + goog.LoadController.prototype.loaded = function() {}; + + + /** + * List of dependencies on which load has been called but which have not + * called loaded on their controller. This includes the current dependency. + * + * @return {!Array} + */ + goog.LoadController.prototype.pending = function() {}; + + + /** + * Registers an object as an ES6 module's exports so that goog.modules may + * require it by path. + * + * @param {string} path Full path of the module. + * @param {?} exports + * @param {string=} opt_closureNamespace Closure namespace to associate with + * this module. + */ + goog.LoadController.prototype.registerEs6ModuleExports = function( + path, exports, opt_closureNamespace) {}; + + + /** + * Sets the current module state. + * + * @param {goog.ModuleType} type Type of module. + */ + goog.LoadController.prototype.setModuleState = function(type) {}; + + + /** + * Clears the current module state. + */ + goog.LoadController.prototype.clearModuleState = function() {}; + + + /** + * Registers a callback to call once the dependency is actually requested + * via goog.require + all of the immediate dependencies have been loaded or + * all other files have been loaded. Allows for lazy loading until + * require'd without pausing dependency loading, which is needed on old IE. + * + * @param {!Function} callback + */ + goog.LoadController.prototype.defer = function(callback) {}; + + + /** + * @return {boolean} + */ + goog.LoadController.prototype.areDepsLoaded = function() {}; + + + /** + * Basic super class for all dependencies Closure Library can load. + * + * This default implementation is designed to load untranspiled, non-module + * scripts in a web broswer. + * + * For transpiled non-goog.module files {@see goog.TranspiledDependency}. + * For goog.modules see {@see goog.GoogModuleDependency}. + * For untranspiled ES6 modules {@see goog.Es6ModuleDependency}. + * + * @param {string} path Absolute path of this script. + * @param {string} relativePath Path of this script relative to goog.basePath. + * @param {!Array} provides goog.provided or goog.module symbols + * in this file. + * @param {!Array} requires goog symbols or relative paths to Closure + * this depends on. + * @param {!Object} loadFlags + * @struct @constructor + */ + goog.Dependency = function( + path, relativePath, provides, requires, loadFlags) { + /** @const */ + this.path = path; + /** @const */ + this.relativePath = relativePath; + /** @const */ + this.provides = provides; + /** @const */ + this.requires = requires; + /** @const */ + this.loadFlags = loadFlags; + /** @private {boolean} */ + this.loaded_ = false; + /** @private {!Array} */ + this.loadCallbacks_ = []; + }; + + + /** + * @return {string} The pathname part of this dependency's path if it is a + * URI. + */ + goog.Dependency.prototype.getPathName = function() { + var pathName = this.path; + var protocolIndex = pathName.indexOf('://'); + if (protocolIndex >= 0) { + pathName = pathName.substring(protocolIndex + 3); + var slashIndex = pathName.indexOf('/'); + if (slashIndex >= 0) { + pathName = pathName.substring(slashIndex + 1); + } + } + return pathName; + }; + + + /** + * @param {function()} callback Callback to fire as soon as this has loaded. + * @final + */ + goog.Dependency.prototype.onLoad = function(callback) { + if (this.loaded_) { + callback(); + } else { + this.loadCallbacks_.push(callback); + } + }; + + + /** + * Marks this dependency as loaded and fires any callbacks registered with + * onLoad. + * @final + */ + goog.Dependency.prototype.loaded = function() { + this.loaded_ = true; + var callbacks = this.loadCallbacks_; + this.loadCallbacks_ = []; + for (var i = 0; i < callbacks.length; i++) { + callbacks[i](); + } + }; + + + /** + * Whether or not document.written / appended script tags should be deferred. + * + * @private {boolean} + */ + goog.Dependency.defer_ = false; + + + /** + * Map of script ready / state change callbacks. Old IE cannot handle putting + * these properties on goog.global. + * + * @private @const {!Object} + */ + goog.Dependency.callbackMap_ = {}; + + + /** + * @param {function(...?):?} callback + * @return {string} + * @private + */ + goog.Dependency.registerCallback_ = function(callback) { + var key = Math.random().toString(32); + goog.Dependency.callbackMap_[key] = callback; + return key; + }; + + + /** + * @param {string} key + * @private + */ + goog.Dependency.unregisterCallback_ = function(key) { + delete goog.Dependency.callbackMap_[key]; + }; + + + /** + * @param {string} key + * @param {...?} var_args + * @private + * @suppress {unusedPrivateMembers} + */ + goog.Dependency.callback_ = function(key, var_args) { + if (key in goog.Dependency.callbackMap_) { + var callback = goog.Dependency.callbackMap_[key]; + var args = []; + for (var i = 1; i < arguments.length; i++) { + args.push(arguments[i]); + } + callback.apply(undefined, args); + } else { + var errorMessage = 'Callback key ' + key + + ' does not exist (was base.js loaded more than once?).'; + throw Error(errorMessage); + } + }; + + + /** + * Starts loading this dependency. This dependency can pause loading if it + * needs to and resume it later via the controller interface. + * + * When this is loaded it should call controller.loaded(). Note that this will + * end up calling the loaded method of this dependency; there is no need to + * call it explicitly. + * + * @param {!goog.LoadController} controller + */ + goog.Dependency.prototype.load = function(controller) { + if (goog.global.CLOSURE_IMPORT_SCRIPT) { + if (goog.global.CLOSURE_IMPORT_SCRIPT(this.path)) { + controller.loaded(); + } else { + controller.pause(); + } + return; + } + + if (!goog.inHtmlDocument_()) { + goog.logToConsole_( + 'Cannot use default debug loader outside of HTML documents.'); + if (this.relativePath == 'deps.js') { + // Some old code is relying on base.js auto loading deps.js failing with + // no error before later setting CLOSURE_IMPORT_SCRIPT. + // CLOSURE_IMPORT_SCRIPT should be set *before* base.js is loaded, or + // CLOSURE_NO_DEPS set to true. + goog.logToConsole_( + 'Consider setting CLOSURE_IMPORT_SCRIPT before loading base.js, ' + + 'or setting CLOSURE_NO_DEPS to true.'); + controller.loaded(); + } else { + controller.pause(); + } + return; + } + + /** @type {!HTMLDocument} */ + var doc = goog.global.document; + + // If the user tries to require a new symbol after document load, + // something has gone terribly wrong. Doing a document.write would + // wipe out the page. This does not apply to the CSP-compliant method + // of writing script tags. + if (doc.readyState == 'complete' && + !goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING) { + // Certain test frameworks load base.js multiple times, which tries + // to write deps.js each time. If that happens, just fail silently. + // These frameworks wipe the page between each load of base.js, so this + // is OK. + var isDeps = /\bdeps.js$/.test(this.path); + if (isDeps) { + controller.loaded(); + return; + } else { + throw Error('Cannot write "' + this.path + '" after document load'); + } + } + + if (!goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING && + goog.isDocumentLoading_()) { + var key = goog.Dependency.registerCallback_(function(script) { + if (!goog.DebugLoader_.IS_OLD_IE_ || script.readyState == 'complete') { + goog.Dependency.unregisterCallback_(key); + controller.loaded(); + } + }); + var nonceAttr = !goog.DebugLoader_.IS_OLD_IE_ && goog.getScriptNonce() ? + ' nonce="' + goog.getScriptNonce() + '"' : + ''; + var event = + goog.DebugLoader_.IS_OLD_IE_ ? 'onreadystatechange' : 'onload'; + var defer = goog.Dependency.defer_ ? 'defer' : ''; + doc.write( + 'world'; + + + +/** @constructor */ +var test = function() {}; + +// Verify that when this module loads the script tag is not modified by +// escaping code in base.js. +test.CLOSING_SCRIPT_TAG = ''; + +exports = test; diff --git a/closure-library/closure/goog/test_module_dep.js b/closure-library/closure/goog/test_module_dep.js new file mode 100644 index 0000000000..b65af6f3f7 --- /dev/null +++ b/closure-library/closure/goog/test_module_dep.js @@ -0,0 +1,26 @@ +// Copyright 2014 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A test file for testing goog.module. + */ + +goog.module('goog.test_module_dep'); +goog.setTestOnly('goog.test_module'); + +/** @type {number} */ +exports.someValue = 1; + +/** @type {function()} */ +exports.someFunction = function() {}; diff --git a/closure-library/closure/goog/testing/assertionfailure.js b/closure-library/closure/goog/testing/assertionfailure.js new file mode 100644 index 0000000000..2903d110a0 --- /dev/null +++ b/closure-library/closure/goog/testing/assertionfailure.js @@ -0,0 +1,58 @@ +// Copyright 2017 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. + +/** + * Utilities intended for testing assertion functions. + */ + +goog.module('goog.testing.safe.assertionFailure'); +goog.setTestOnly(); + +const asserts = goog.require('goog.asserts'); +const testingAsserts = goog.require('goog.testing.asserts'); + +/** + * Tests that f raises exactaly one AssertionError and runs f while disabling + * assertion errors. This is only intended to use in a few test files that is + * guaranteed that will not affect anything for convenience. It is not intended + * for broader consumption outside of those test files. We do not want to + * encourage this pattern. + * + * @param {function():*} f function with a failing assertion. + * @param {string=} opt_message error message the expected error should contain + * @param {number=} opt_number of time the assertion should throw. Default is 1. + * @return {*} the return value of f. + */ +exports.withAssertionFailure = function(f, opt_message, opt_number) { + try { + if (!opt_number) { + opt_number = 1; + } + var assertions = 0; + asserts.setErrorHandler(function(e) { + asserts.assertInstanceof( + e, asserts.AssertionError, 'A none assertion failure is thrown'); + if (opt_message) { + testingAsserts.assertContains(opt_message, e.message); + } + assertions += 1; + }); + var result = f(); + asserts.assert( + assertions == opt_number, '%d assertion failed.', assertions); + return result; + } finally { + asserts.setErrorHandler(asserts.DEFAULT_ERROR_HANDLER); + } +}; diff --git a/closure-library/closure/goog/testing/asserts.js b/closure-library/closure/goog/testing/asserts.js new file mode 100644 index 0000000000..8bf24f298f --- /dev/null +++ b/closure-library/closure/goog/testing/asserts.js @@ -0,0 +1,1545 @@ +// Copyright 2010 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.testing.asserts'); +goog.setTestOnly(); + +goog.require('goog.testing.JsUnitException'); + +// TODO(user): Copied from JsUnit with some small modifications, we should +// reimplement the asserters. + +var DOUBLE_EQUALITY_PREDICATE = function(var1, var2) { + return var1 == var2; +}; +var JSUNIT_UNDEFINED_VALUE = void 0; +var TO_STRING_EQUALITY_PREDICATE = function(var1, var2) { + return var1.toString() === var2.toString(); +}; +var OUTPUT_NEW_LINE_THRESHOLD = 40; + + +/** @typedef {function(?, ?):boolean} */ +var PredicateFunctionType; + + +/** + * @const {{ + * String : PredicateFunctionType, + * Number : PredicateFunctionType, + * Boolean : PredicateFunctionType, + * Date : PredicateFunctionType, + * RegExp : PredicateFunctionType, + * Function : PredicateFunctionType + * }} + */ +var PRIMITIVE_EQUALITY_PREDICATES = { + 'String': DOUBLE_EQUALITY_PREDICATE, + 'Number': DOUBLE_EQUALITY_PREDICATE, + 'Boolean': DOUBLE_EQUALITY_PREDICATE, + 'Date': function(date1, date2) { return date1.getTime() == date2.getTime(); }, + 'RegExp': TO_STRING_EQUALITY_PREDICATE, + 'Function': TO_STRING_EQUALITY_PREDICATE +}; + + +/** + * Compares equality of two numbers, allowing them to differ up to a given + * tolerance. + * @param {number} var1 A number. + * @param {number} var2 A number. + * @param {number} tolerance the maximum allowed difference. + * @return {boolean} Whether the two variables are sufficiently close. + * @private + */ +goog.testing.asserts.numberRoughEqualityPredicate_ = function( + var1, var2, tolerance) { + return Math.abs(var1 - var2) <= tolerance; +}; + + +/** + * @type {!Object} + * @private + */ +goog.testing.asserts.primitiveRoughEqualityPredicates_ = { + 'Number': goog.testing.asserts.numberRoughEqualityPredicate_ +}; + + +var _trueTypeOf = function(something) { + var result = typeof something; + try { + switch (result) { + case 'string': + break; + case 'boolean': + break; + case 'number': + break; + case 'object': + if (something == null) { + result = 'null'; + break; + } + case 'function': + switch (something.constructor) { + case new String('').constructor: + result = 'String'; + break; + case new Boolean(true).constructor: + result = 'Boolean'; + break; + case new Number(0).constructor: + result = 'Number'; + break; + case new Array().constructor: + result = 'Array'; + break; + case new RegExp().constructor: + result = 'RegExp'; + break; + case new Date().constructor: + result = 'Date'; + break; + case Function: + result = 'Function'; + break; + default: + var m = + something.constructor.toString().match(/function\s*([^( ]+)\(/); + if (m) { + result = m[1]; + } else { + break; + } + } + break; + } + } catch (e) { + } finally { + result = result.substr(0, 1).toUpperCase() + result.substr(1); + } + return result; +}; + +var _displayStringForValue = function(aVar) { + var result; + try { + result = '<' + String(aVar) + '>'; + } catch (ex) { + result = ''; + // toString does not work on this object :-( + } + if (!(aVar === null || aVar === JSUNIT_UNDEFINED_VALUE)) { + result += ' (' + _trueTypeOf(aVar) + ')'; + } + return result; +}; + +/** @param {?} failureMessage */ +var fail = goog.testing.asserts.fail = function(failureMessage) { + goog.testing.asserts.raiseException('Call to fail()', failureMessage); +}; + +var argumentsIncludeComments = function(expectedNumberOfNonCommentArgs, args) { + return args.length == expectedNumberOfNonCommentArgs + 1; +}; + +var commentArg = function(expectedNumberOfNonCommentArgs, args) { + if (argumentsIncludeComments(expectedNumberOfNonCommentArgs, args)) { + return args[0]; + } + + return null; +}; + +var nonCommentArg = function( + desiredNonCommentArgIndex, expectedNumberOfNonCommentArgs, args) { + return argumentsIncludeComments(expectedNumberOfNonCommentArgs, args) ? + args[desiredNonCommentArgIndex] : + args[desiredNonCommentArgIndex - 1]; +}; + +var _validateArguments = function(expectedNumberOfNonCommentArgs, args) { + var valid = args.length == expectedNumberOfNonCommentArgs || + args.length == expectedNumberOfNonCommentArgs + 1 && + goog.isString(args[0]); + if (!valid) { + goog.testing.asserts.raiseException( + 'Incorrect arguments passed to assert function.\n' + + 'Expected ' + expectedNumberOfNonCommentArgs + ' argument(s) plus ' + + 'optional comment; got ' + args.length + '.'); + } + +}; + +/** + * @return {?} goog.testing.TestCase or null + * We suppress the lint error and we explicitly do not goog.require() + * goog.testing.TestCase to avoid a build time dependency cycle. + * @suppress {missingRequire|undefinedNames|undefinedVars|missingProperties} + * @private + */ +var _getCurrentTestCase = function() { + // Some users of goog.testing.asserts do not use goog.testing.TestRunner and + // they do not include goog.testing.TestCase. Exceptions will not be + // completely correct for these users. + if (!goog.testing.TestCase) { + if (goog.global.console) { + goog.global.console.error( + 'Missing goog.testing.TestCase, ' + + 'add /* @suppress {extraRequire} */' + + 'goog.require(\'goog.testing.TestCase\''); + } + return null; + } + return goog.testing.TestCase.getActiveTestCase(); +}; + +var _assert = function(comment, booleanValue, failureMessage) { + if (!booleanValue) { + goog.testing.asserts.raiseException(comment, failureMessage); + } +}; + + +/** + * @param {*} expected The expected value. + * @param {*} actual The actual value. + * @return {string} A failure message of the values don't match. + * @private + */ +goog.testing.asserts.getDefaultErrorMsg_ = function(expected, actual) { + var expectedDisplayString = _displayStringForValue(expected); + var actualDisplayString = _displayStringForValue(actual); + var shouldUseNewLines = + expectedDisplayString.length > OUTPUT_NEW_LINE_THRESHOLD || + actualDisplayString.length > OUTPUT_NEW_LINE_THRESHOLD; + var msg = [ + 'Expected', expectedDisplayString, 'but was', actualDisplayString + ].join(shouldUseNewLines ? '\n' : ' '); + + if ((typeof expected == 'string') && (typeof actual == 'string')) { + // Try to find a human-readable difference. + var limit = Math.min(expected.length, actual.length); + var commonPrefix = 0; + while (commonPrefix < limit && + expected.charAt(commonPrefix) == actual.charAt(commonPrefix)) { + commonPrefix++; + } + + var commonSuffix = 0; + while (commonSuffix < limit && + expected.charAt(expected.length - commonSuffix - 1) == + actual.charAt(actual.length - commonSuffix - 1)) { + commonSuffix++; + } + + if (commonPrefix + commonSuffix > limit) { + commonSuffix = 0; + } + + if (commonPrefix > 2 || commonSuffix > 2) { + var printString = function(str) { + var startIndex = Math.max(0, commonPrefix - 2); + var endIndex = Math.min(str.length, str.length - (commonSuffix - 2)); + return (startIndex > 0 ? '...' : '') + + str.substring(startIndex, endIndex) + + (endIndex < str.length ? '...' : ''); + }; + + var expectedPrinted = printString(expected); + var expectedActual = printString(actual); + var shouldUseNewLinesInDiff = + expectedPrinted.length > OUTPUT_NEW_LINE_THRESHOLD || + expectedActual.length > OUTPUT_NEW_LINE_THRESHOLD; + msg += '\nDifference was at position ' + commonPrefix + '. ' + [ + 'Expected', '[' + expectedPrinted + ']', 'vs. actual', + '[' + expectedActual + ']' + ].join(shouldUseNewLinesInDiff ? '\n' : ' '); + } + } + return msg; +}; + + +/** + * @param {*} a The value to assert (1 arg) or debug message (2 args). + * @param {*=} opt_b The value to assert (2 args only). + */ +var assert = goog.testing.asserts.assert = function(a, opt_b) { + _validateArguments(1, arguments); + var comment = commentArg(1, arguments); + var booleanValue = nonCommentArg(1, 1, arguments); + + _assert( + comment, goog.isBoolean(booleanValue), 'Bad argument to assert(boolean)'); + _assert(comment, booleanValue, 'Call to assert(boolean) with false'); +}; + + +/** + * Asserts that the function throws an error. + * + * @param {!(string|Function)} a The assertion comment or the function to call. + * @param {!Function=} opt_b The function to call (if the first argument of + * `assertThrows` was the comment). + * @return {*} The error thrown by the function. + * @throws {goog.testing.JsUnitException} If the assertion failed. + */ +var assertThrows = goog.testing.asserts.assertThrows = function(a, opt_b) { + _validateArguments(1, arguments); + var func = nonCommentArg(1, 1, arguments); + var comment = commentArg(1, arguments); + _assert( + comment, typeof func == 'function', + 'Argument passed to assertThrows is not a function'); + + try { + func(); + } catch (e) { + goog.testing.asserts.removeOperaStacktrace_(e); + + var testCase = _getCurrentTestCase(); + if (e && e['isJsUnitException'] && testCase && + testCase.failOnUnreportedAsserts) { + goog.testing.asserts.raiseException( + comment, + 'Function passed to assertThrows caught a JsUnitException (usually ' + + 'from an assert or call to fail()). If this is expected, use ' + + 'assertThrowsJsUnitException instead.'); + } + + return e; + } + goog.testing.asserts.raiseException( + comment, 'No exception thrown from function passed to assertThrows'); +}; + + +/** + * Removes a stacktrace from an Error object for Opera 10.0. + * @param {*} e + * @private + */ +goog.testing.asserts.removeOperaStacktrace_ = function(e) { + if (goog.isObject(e) && goog.isString(e['stacktrace']) && + goog.isString(e['message'])) { + var startIndex = e['message'].length - e['stacktrace'].length; + if (e['message'].indexOf(e['stacktrace'], startIndex) == startIndex) { + e['message'] = e['message'].substr(0, startIndex - 14); + } + } +}; + + +/** + * Asserts that the function does not throw an error. + * + * @param {!(string|Function)} a The assertion comment or the function to call. + * @param {!Function=} opt_b The function to call (if the first argument of + * `assertNotThrows` was the comment). + * @return {*} The return value of the function. + * @throws {goog.testing.JsUnitException} If the assertion failed. + */ +var assertNotThrows = goog.testing.asserts.assertNotThrows = function( + a, opt_b) { + _validateArguments(1, arguments); + var comment = commentArg(1, arguments); + var func = nonCommentArg(1, 1, arguments); + _assert( + comment, typeof func == 'function', + 'Argument passed to assertNotThrows is not a function'); + + try { + return func(); + } catch (e) { + comment = comment ? (comment + '\n') : ''; + comment += 'A non expected exception was thrown from function passed to ' + + 'assertNotThrows'; + // Some browsers don't have a stack trace so at least have the error + // description. + var stackTrace = e['stack'] || e['stacktrace'] || e.toString(); + goog.testing.asserts.raiseException(comment, stackTrace); + } +}; + + +/** + * Asserts that the given callback function results in a JsUnitException when + * called, and that the resulting failure message matches the given expected + * message. + * @param {function() : void} callback Function to be run expected to result + * in a JsUnitException (usually contains a call to an assert). + * @param {string=} opt_expectedMessage Failure message expected to be given + * with the exception. + * @return {!goog.testing.JsUnitException} The error thrown by the function. + * @throws {goog.testing.JsUnitException} If the function did not throw a + * JsUnitException. + */ +var assertThrowsJsUnitException = goog.testing.asserts + .assertThrowsJsUnitException = function( + callback, opt_expectedMessage) { + try { + callback(); + } catch (e) { + var testCase = _getCurrentTestCase(); + if (testCase) { + testCase.invalidateAssertionException(e); + } else { + goog.global.console.error( + 'Failed to remove expected exception: no test case is installed.'); + } + + if (!e.isJsUnitException) { + goog.testing.asserts.fail('Expected a JsUnitException'); + } + + if (typeof opt_expectedMessage != 'undefined' && + e.message != opt_expectedMessage) { + goog.testing.asserts.fail( + 'Expected message [' + opt_expectedMessage + '] but got [' + + e.message + ']'); + } + + return e; + } + + var msg = 'Expected a failure'; + if (typeof opt_expectedMessage != 'undefined') { + msg += ': ' + opt_expectedMessage; + } + throw new goog.testing.JsUnitException(msg); +}; + + +/** + * Asserts that the IThenable rejects. + * + * This is useful for asserting that async functions throw, like an asynchronous + * assertThrows. Example: + * + * ``` + * async function shouldThrow() { throw new Error('error!'); } + * async function testShouldThrow() { + * const error = await assertRejects(shouldThrow()); + * assertEquals('error!', error.message); + * } + * ``` + * + * @param {!(string|IThenable)} a The assertion comment or the IThenable. + * @param {!IThenable=} opt_b The IThenable (if the first argument of + * `assertRejects` was the comment). + * @return {!IThenable<*>} A child IThenable which resolves with the error that + * the passed in IThenable rejects with. This IThenable will reject if the + * passed in IThenable does not reject. + */ +var assertRejects = goog.testing.asserts.assertRejects = function(a, opt_b) { + _validateArguments(1, arguments); + var thenable = nonCommentArg(1, 1, arguments); + var comment = commentArg(1, arguments); + _assert( + comment, goog.isObject(thenable) && goog.isFunction(thenable.then), + 'Argument passed to assertRejects is not an IThenable'); + + return thenable.then( + function() { + goog.testing.asserts.raiseException( + comment, 'IThenable passed into assertRejects did not reject'); + }, + function(e) { + goog.testing.asserts.removeOperaStacktrace_(e); + return e; + }); +}; + + +/** + * @param {*} a The value to assert (1 arg) or debug message (2 args). + * @param {*=} opt_b The value to assert (2 args only). + */ +var assertTrue = goog.testing.asserts.assertTrue = function(a, opt_b) { + _validateArguments(1, arguments); + var comment = commentArg(1, arguments); + var booleanValue = nonCommentArg(1, 1, arguments); + + _assert( + comment, goog.isBoolean(booleanValue), + 'Bad argument to assertTrue(boolean)'); + _assert(comment, booleanValue, 'Call to assertTrue(boolean) with false'); +}; + + +/** + * @param {*} a The value to assert (1 arg) or debug message (2 args). + * @param {*=} opt_b The value to assert (2 args only). + */ +var assertFalse = goog.testing.asserts.assertFalse = function(a, opt_b) { + _validateArguments(1, arguments); + var comment = commentArg(1, arguments); + var booleanValue = nonCommentArg(1, 1, arguments); + + _assert( + comment, goog.isBoolean(booleanValue), + 'Bad argument to assertFalse(boolean)'); + _assert(comment, !booleanValue, 'Call to assertFalse(boolean) with true'); +}; + + +/** + * @param {*} a The expected value (2 args) or the debug message (3 args). + * @param {*} b The actual value (2 args) or the expected value (3 args). + * @param {*=} opt_c The actual value (3 args only). + */ +var assertEquals = goog.testing.asserts.assertEquals = function(a, b, opt_c) { + _validateArguments(2, arguments); + var var1 = nonCommentArg(1, 2, arguments); + var var2 = nonCommentArg(2, 2, arguments); + _assert( + commentArg(2, arguments), var1 === var2, + goog.testing.asserts.getDefaultErrorMsg_(var1, var2)); +}; + + +/** + * @param {*} a The expected value (2 args) or the debug message (3 args). + * @param {*} b The actual value (2 args) or the expected value (3 args). + * @param {*=} opt_c The actual value (3 args only). + */ +var assertNotEquals = goog.testing.asserts.assertNotEquals = function( + a, b, opt_c) { + _validateArguments(2, arguments); + var var1 = nonCommentArg(1, 2, arguments); + var var2 = nonCommentArg(2, 2, arguments); + _assert( + commentArg(2, arguments), var1 !== var2, + 'Expected not to be ' + _displayStringForValue(var2)); +}; + + +/** + * @param {*} a The value to assert (1 arg) or debug message (2 args). + * @param {*=} opt_b The value to assert (2 args only). + */ +var assertNull = goog.testing.asserts.assertNull = function(a, opt_b) { + _validateArguments(1, arguments); + var aVar = nonCommentArg(1, 1, arguments); + _assert( + commentArg(1, arguments), aVar === null, + goog.testing.asserts.getDefaultErrorMsg_(null, aVar)); +}; + + +/** + * @param {*} a The value to assert (1 arg) or debug message (2 args). + * @param {*=} opt_b The value to assert (2 args only). + */ +var assertNotNull = goog.testing.asserts.assertNotNull = function(a, opt_b) { + _validateArguments(1, arguments); + var aVar = nonCommentArg(1, 1, arguments); + _assert( + commentArg(1, arguments), aVar !== null, + 'Expected not to be ' + _displayStringForValue(null)); +}; + + +/** + * @param {*} a The value to assert (1 arg) or debug message (2 args). + * @param {*=} opt_b The value to assert (2 args only). + */ +var assertUndefined = goog.testing.asserts.assertUndefined = function( + a, opt_b) { + _validateArguments(1, arguments); + var aVar = nonCommentArg(1, 1, arguments); + _assert( + commentArg(1, arguments), aVar === JSUNIT_UNDEFINED_VALUE, + goog.testing.asserts.getDefaultErrorMsg_(JSUNIT_UNDEFINED_VALUE, aVar)); +}; + + +/** + * @param {*} a The value to assert (1 arg) or debug message (2 args). + * @param {*=} opt_b The value to assert (2 args only). + */ +var assertNotUndefined = goog.testing.asserts.assertNotUndefined = function( + a, opt_b) { + _validateArguments(1, arguments); + var aVar = nonCommentArg(1, 1, arguments); + _assert( + commentArg(1, arguments), aVar !== JSUNIT_UNDEFINED_VALUE, + 'Expected not to be ' + _displayStringForValue(JSUNIT_UNDEFINED_VALUE)); +}; + +/** + * @param {*} a The value to assert (1 arg) or debug message (2 args). + * @param {*=} opt_b The value to assert (2 args only). + */ +var assertNullOrUndefined = + goog.testing.asserts.assertNullOrUndefined = function(a, opt_b) { + _validateArguments(1, arguments); + var aVar = nonCommentArg(1, 1, arguments); + _assert( + commentArg(1, arguments), aVar == null, + 'Expected ' + _displayStringForValue(null) + ' or ' + + _displayStringForValue(JSUNIT_UNDEFINED_VALUE) + ' but was ' + + _displayStringForValue(aVar)); + }; + +/** + * @param {*} a The value to assert (1 arg) or debug message (2 args). + * @param {*=} opt_b The value to assert (2 args only). + */ +var assertNotNullNorUndefined = + goog.testing.asserts.assertNotNullNorUndefined = function(a, opt_b) { + _validateArguments(1, arguments); + goog.testing.asserts.assertNotNull.apply(null, arguments); + goog.testing.asserts.assertNotUndefined.apply(null, arguments); + }; + + +/** + * @param {*} a The value to assert (1 arg) or debug message (2 args). + * @param {*=} opt_b The value to assert (2 args only). + */ +var assertNonEmptyString = goog.testing.asserts.assertNonEmptyString = function( + a, opt_b) { + _validateArguments(1, arguments); + var aVar = nonCommentArg(1, 1, arguments); + _assert( + commentArg(1, arguments), aVar !== JSUNIT_UNDEFINED_VALUE && + aVar !== null && typeof aVar == 'string' && aVar !== '', + 'Expected non-empty string but was ' + _displayStringForValue(aVar)); +}; + + +/** + * @param {*} a The value to assert (1 arg) or debug message (2 args). + * @param {*=} opt_b The value to assert (2 args only). + */ +var assertNaN = goog.testing.asserts.assertNaN = function(a, opt_b) { + _validateArguments(1, arguments); + var aVar = nonCommentArg(1, 1, arguments); + _assert(commentArg(1, arguments), isNaN(aVar), 'Expected NaN'); +}; + + +/** + * @param {*} a The value to assert (1 arg) or debug message (2 args). + * @param {*=} opt_b The value to assert (2 args only). + */ +var assertNotNaN = goog.testing.asserts.assertNotNaN = function(a, opt_b) { + _validateArguments(1, arguments); + var aVar = nonCommentArg(1, 1, arguments); + _assert(commentArg(1, arguments), !isNaN(aVar), 'Expected not NaN'); +}; + + +/** + * The return value of the equality predicate passed to findDifferences below, + * in cases where the predicate can't test the input variables for equality. + * @type {?string} + */ +goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS = null; + + +/** + * The return value of the equality predicate passed to findDifferences below, + * in cases where the input vriables are equal. + * @type {?string} + */ +goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL = ''; + + +/** + * Determines if two items of any type match, and formulates an error message + * if not. + * @param {*} expected Expected argument to match. + * @param {*} actual Argument as a result of performing the test. + * @param {(function(string, *, *): ?string)=} opt_equalityPredicate An optional + * function that can be used to check equality of variables. It accepts 3 + * arguments: type-of-variables, var1, var2 (in that order) and returns an + * error message if the variables are not equal, + * goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL if the variables + * are equal, or + * goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS if the predicate + * couldn't check the input variables. The function will be called only if + * the types of var1 and var2 are identical. + * @return {?string} Null on success, error message on failure. + */ +goog.testing.asserts.findDifferences = function( + expected, actual, opt_equalityPredicate) { + var failures = []; + // True if there a generic error at the root (with no path). If so, we should + // fail, but not add to the failures array (because it will be included at the + // top anyway). + var rootFailed = false; + var seen1 = []; + var seen2 = []; + + // To avoid infinite recursion when the two parameters are self-referential + // along the same path of properties, keep track of the object pairs already + // seen in this call subtree, and abort when a cycle is detected. + function innerAssertWithCycleCheck(var1, var2, path) { + // This is used for testing, so we can afford to be slow (but more + // accurate). So we just check whether var1 is in seen1. If we + // found var1 in index i, we simply need to check whether var2 is + // in seen2[i]. If it is, we do not recurse to check var1/var2. If + // it isn't, we know that the structures of the two objects must be + // different. + // + // This is based on the fact that values at index i in seen1 and + // seen2 will be checked for equality eventually (when + // innerAssertImplementation(seen1[i], seen2[i], path) finishes). + for (var i = 0; i < seen1.length; ++i) { + var match1 = seen1[i] === var1; + var match2 = seen2[i] === var2; + if (match1 || match2) { + if (!match1 || !match2) { + // Asymmetric cycles, so the objects have different structure. + failures.push('Asymmetric cycle detected at ' + path); + } + return; + } + } + + seen1.push(var1); + seen2.push(var2); + innerAssertImplementation(var1, var2, path); + seen1.pop(); + seen2.pop(); + } + + var equalityPredicate = opt_equalityPredicate || function(type, var1, var2) { + var typedPredicate = PRIMITIVE_EQUALITY_PREDICATES[type]; + if (!typedPredicate) { + return goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS; + } + var equal = typedPredicate(var1, var2); + return equal ? goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL : + goog.testing.asserts.getDefaultErrorMsg_(var1, var2); + }; + + /** + * @param {*} var1 An item in the expected object. + * @param {*} var2 The corresponding item in the actual object. + * @param {string} path Their path in the objects. + * @suppress {missingProperties} The map_ property is unknown to the compiler + * unless goog.structs.Map is loaded. + */ + function innerAssertImplementation(var1, var2, path) { + if (var1 === var2) { + return; + } + + var typeOfVar1 = _trueTypeOf(var1); + var typeOfVar2 = _trueTypeOf(var2); + + if (typeOfVar1 == typeOfVar2) { + var isArray = typeOfVar1 == 'Array'; + var errorMessage = equalityPredicate(typeOfVar1, var1, var2); + if (errorMessage != + goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS) { + if (errorMessage != + goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL) { + if (path) { + failures.push(path + ': ' + errorMessage); + } else { + rootFailed = true; + } + } + } else if (isArray && var1.length != var2.length) { + failures.push( + (path ? path + ': ' : '') + 'Expected ' + var1.length + + '-element array ' + + 'but got a ' + var2.length + '-element array'); + } else if (typeOfVar1 == 'String') { + // If the comparer cannot process strings (eg, roughlyEquals). + if (var1 != var2) { + if (path) { + failures.push( + path + ': ' + + goog.testing.asserts.getDefaultErrorMsg_(var1, var2)); + } else { + rootFailed = true; + } + } + } else { + var childPath = path + (isArray ? '[%s]' : (path ? '.%s' : '%s')); + // These type checks do not use _trueTypeOf because that does not work + // for polyfilled Map/Set. Note that these checks may potentially fail + // if var1 comes from a different window. + if ((typeof Map != 'undefined' && var1 instanceof Map) || + (typeof Set != 'undefined' && var1 instanceof Set)) { + var1.forEach(function(value, key) { + if (var2.has(key)) { + // For a map, the values must be compared, but with Set, checking + // that the second set contains the first set's "keys" is + // sufficient. + if (var2.get) { + innerAssertWithCycleCheck( + // NOTE: replace will call functions, so stringify eagerly. + value, var2.get(key), childPath.replace('%s', String(key))); + } + } else { + failures.push( + key + ' not present in actual ' + (path || typeOfVar2)); + } + }); + + var2.forEach(function(value, key) { + if (!var1.has(key)) { + failures.push( + key + ' not present in expected ' + (path || typeOfVar1)); + } + }); + } else if (!var1['__iterator__']) { + // if an object has an __iterator__ property, we have no way of + // actually inspecting its raw properties, and JS 1.7 doesn't + // overload [] to make it possible for someone to generically + // use what the iterator returns to compare the object-managed + // properties. This gets us into deep poo with things like + // goog.structs.Map, at least on systems that support iteration. + for (var prop in var1) { + if (isArray && goog.testing.asserts.isArrayIndexProp_(prop)) { + // Skip array indices for now. We'll handle them later. + continue; + } + + if (prop in var2) { + innerAssertWithCycleCheck( + var1[prop], var2[prop], childPath.replace('%s', prop)); + } else { + failures.push( + 'property ' + prop + ' not present in actual ' + + (path || typeOfVar2)); + } + } + // make sure there aren't properties in var2 that are missing + // from var1. if there are, then by definition they don't + // match. + for (var prop in var2) { + if (isArray && goog.testing.asserts.isArrayIndexProp_(prop)) { + // Skip array indices for now. We'll handle them later. + continue; + } + + if (!(prop in var1)) { + failures.push( + 'property ' + prop + ' not present in expected ' + + (path || typeOfVar1)); + } + } + + // Handle array indices by iterating from 0 to arr.length. + // + // Although all browsers allow holes in arrays, browsers + // are inconsistent in what they consider a hole. For example, + // "[0,undefined,2]" has a hole on IE but not on Firefox. + // + // Because our style guide bans for...in iteration over arrays, + // we assume that most users don't care about holes in arrays, + // and that it is ok to say that a hole is equivalent to a slot + // populated with 'undefined'. + if (isArray) { + for (prop = 0; prop < var1.length; prop++) { + innerAssertWithCycleCheck( + var1[prop], var2[prop], + childPath.replace('%s', String(prop))); + } + } + } else { + // special-case for closure objects that have iterators + if (goog.isFunction(var1.equals)) { + // use the object's own equals function, assuming it accepts an + // object and returns a boolean + if (!var1.equals(var2)) { + failures.push( + 'equals() returned false for ' + (path || typeOfVar1)); + } + } else if (var1.map_) { + // assume goog.structs.Map or goog.structs.Set, where comparing + // their private map_ field is sufficient + innerAssertWithCycleCheck( + var1.map_, var2.map_, childPath.replace('%s', 'map_')); + } else { + // else die, so user knows we can't do anything + failures.push( + 'unable to check ' + (path || typeOfVar1) + + ' for equality: it has an iterator we do not ' + + 'know how to handle. please add an equals method'); + } + } + } + } else if (path) { + failures.push( + path + ': ' + goog.testing.asserts.getDefaultErrorMsg_(var1, var2)); + } else { + rootFailed = true; + } + } + + innerAssertWithCycleCheck(expected, actual, ''); + + if (rootFailed) { + return goog.testing.asserts.getDefaultErrorMsg_(expected, actual); + } + return failures.length == 0 ? null : goog.testing.asserts.getDefaultErrorMsg_( + expected, actual) + + '\n ' + failures.join('\n '); +}; + + +/** + * Notes: + * Object equality has some nasty browser quirks, and this implementation is + * not 100% correct. For example, + * + * + * var a = [0, 1, 2]; + * var b = [0, 1, 2]; + * delete a[1]; + * b[1] = undefined; + * assertObjectEquals(a, b); // should fail, but currently passes + * + * + * See asserts_test.html for more interesting edge cases. + * + * The first comparison object provided is the expected value, the second is + * the actual. + * + * @param {*} a Assertion message or comparison object. + * @param {*} b Comparison object. + * @param {*=} opt_c Comparison object, if an assertion message was provided. + */ +var assertObjectEquals = goog.testing.asserts.assertObjectEquals = function( + a, b, opt_c) { + _validateArguments(2, arguments); + var v1 = nonCommentArg(1, 2, arguments); + var v2 = nonCommentArg(2, 2, arguments); + var failureMessage = commentArg(2, arguments) ? commentArg(2, arguments) : ''; + var differences = goog.testing.asserts.findDifferences(v1, v2); + + _assert(failureMessage, !differences, differences); +}; + + +/** + * Similar to assertObjectEquals above, but accepts a tolerance margin. + * + * @param {*} a Assertion message or comparison object. + * @param {*} b Comparison object. + * @param {*} c Comparison object or tolerance. + * @param {*=} opt_d Tolerance, if an assertion message was provided. + */ +var assertObjectRoughlyEquals = goog.testing.asserts.assertObjectRoughlyEquals = + function(a, b, c, opt_d) { + _validateArguments(3, arguments); + var v1 = nonCommentArg(1, 3, arguments); + var v2 = nonCommentArg(2, 3, arguments); + var tolerance = nonCommentArg(3, 3, arguments); + var failureMessage = commentArg(3, arguments) ? commentArg(3, arguments) : ''; + var equalityPredicate = function(type, var1, var2) { + var typedPredicate = + goog.testing.asserts.primitiveRoughEqualityPredicates_[type]; + if (!typedPredicate) { + return goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS; + } + var equal = typedPredicate(var1, var2, tolerance); + return equal ? goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL : + goog.testing.asserts.getDefaultErrorMsg_(var1, var2) + + ' which was more than ' + tolerance + ' away'; + }; + var differences = + goog.testing.asserts.findDifferences(v1, v2, equalityPredicate); + + _assert(failureMessage, !differences, differences); +}; + + +/** + * Compares two arbitrary objects for non-equalness. + * + * All the same caveats as for assertObjectEquals apply here: + * Undefined values may be confused for missing values, or vice versa. + * + * @param {*} a Assertion message or comparison object. + * @param {*} b Comparison object. + * @param {*=} opt_c Comparison object, if an assertion message was provided. + */ +var assertObjectNotEquals = + goog.testing.asserts.assertObjectNotEquals = function(a, b, opt_c) { + _validateArguments(2, arguments); + var v1 = nonCommentArg(1, 2, arguments); + var v2 = nonCommentArg(2, 2, arguments); + var failureMessage = + commentArg(2, arguments) ? commentArg(2, arguments) : ''; + var differences = goog.testing.asserts.findDifferences(v1, v2); + + _assert(failureMessage, differences, 'Objects should not be equal'); + }; + + +/** + * Compares two arrays ignoring negative indexes and extra properties on the + * array objects. Use case: Internet Explorer adds the index, lastIndex and + * input enumerable fields to the result of string.match(/regexp/g), which makes + * assertObjectEquals fail. + * @param {*} a The expected array (2 args) or the debug message (3 args). + * @param {*} b The actual array (2 args) or the expected array (3 args). + * @param {*=} opt_c The actual array (3 args only). + */ +var assertArrayEquals = goog.testing.asserts.assertArrayEquals = function( + a, b, opt_c) { + _validateArguments(2, arguments); + var v1 = nonCommentArg(1, 2, arguments); + var v2 = nonCommentArg(2, 2, arguments); + var failureMessage = commentArg(2, arguments) ? commentArg(2, arguments) : ''; + + var typeOfVar1 = _trueTypeOf(v1); + _assert( + failureMessage, typeOfVar1 == 'Array', + 'Expected an array for assertArrayEquals but found a ' + typeOfVar1); + + var typeOfVar2 = _trueTypeOf(v2); + _assert( + failureMessage, typeOfVar2 == 'Array', + 'Expected an array for assertArrayEquals but found a ' + typeOfVar2); + + goog.testing.asserts.assertObjectEquals( + failureMessage, Array.prototype.concat.call(v1), + Array.prototype.concat.call(v2)); +}; + + +/** + * Compares two objects that can be accessed like an array and assert that + * each element is equal. + * @param {string|Object} a Failure message (3 arguments) + * or object #1 (2 arguments). + * @param {Object} b Object #2 (2 arguments) or object #1 (3 arguments). + * @param {Object=} opt_c Object #2 (3 arguments). + */ +var assertElementsEquals = goog.testing.asserts.assertElementsEquals = function( + a, b, opt_c) { + _validateArguments(2, arguments); + + var v1 = nonCommentArg(1, 2, arguments); + var v2 = nonCommentArg(2, 2, arguments); + var failureMessage = commentArg(2, arguments) ? commentArg(2, arguments) : ''; + + if (!v1) { + goog.testing.asserts.assert(failureMessage, !v2); + } else { + goog.testing.asserts.assertEquals( + 'length mismatch: ' + failureMessage, v1.length, v2.length); + for (var i = 0; i < v1.length; ++i) { + goog.testing.asserts.assertEquals( + 'mismatch at index ' + i + ': ' + failureMessage, v1[i], v2[i]); + } + } +}; + + +/** + * Compares two objects that can be accessed like an array and assert that + * each element is roughly equal. + * @param {string|Object} a Failure message (4 arguments) + * or object #1 (3 arguments). + * @param {Object} b Object #1 (4 arguments) or object #2 (3 arguments). + * @param {Object|number} c Object #2 (4 arguments) or tolerance (3 arguments). + * @param {number=} opt_d tolerance (4 arguments). + */ +var assertElementsRoughlyEqual = + goog.testing.asserts.assertElementsRoughlyEqual = function(a, b, c, opt_d) { + _validateArguments(3, arguments); + + var v1 = nonCommentArg(1, 3, arguments); + var v2 = nonCommentArg(2, 3, arguments); + var tolerance = nonCommentArg(3, 3, arguments); + var failureMessage = + commentArg(3, arguments) ? commentArg(3, arguments) : ''; + + if (!v1) { + goog.testing.asserts.assert(failureMessage, !v2); + } else { + goog.testing.asserts.assertEquals( + 'length mismatch: ' + failureMessage, v1.length, v2.length); + for (var i = 0; i < v1.length; ++i) { + goog.testing.asserts.assertRoughlyEquals( + failureMessage, v1[i], v2[i], tolerance); + } + } + }; + + +/** + * Compares elements of two array-like or iterable objects using strict equality + * without taking their order into account. + * @param {string|!IArrayLike|!Iterable} a Assertion message or the + * expected elements. + * @param {!IArrayLike|!Iterable} b Expected elements or the actual + * elements. + * @param {!IArrayLike|!Iterable=} opt_c Actual elements. + */ +var assertSameElements = goog.testing.asserts.assertSameElements = function( + a, b, opt_c) { + _validateArguments(2, arguments); + var expected = nonCommentArg(1, 2, arguments); + var actual = nonCommentArg(2, 2, arguments); + var message = commentArg(2, arguments); + + goog.testing.asserts.assertTrue( + 'Value of \'expected\' should be array-like or iterable', + goog.testing.asserts.isArrayLikeOrIterable_(expected)); + + goog.testing.asserts.assertTrue( + 'Value of \'actual\' should be array-like or iterable', + goog.testing.asserts.isArrayLikeOrIterable_(actual)); + + // Clones expected and actual and converts them to real arrays. + expected = goog.testing.asserts.toArray_(expected); + actual = goog.testing.asserts.toArray_(actual); + // TODO(user): It would be great to show only the difference + // between the expected and actual elements. + _assert( + message, expected.length == actual.length, 'Expected ' + expected.length + + ' elements: [' + expected + '], ' + + 'got ' + actual.length + ' elements: [' + actual + ']'); + + var toFind = goog.testing.asserts.toArray_(expected); + for (var i = 0; i < actual.length; i++) { + var index = goog.testing.asserts.indexOf_(toFind, actual[i]); + _assert( + message, index != -1, + 'Expected [' + expected + '], got [' + actual + ']'); + toFind.splice(index, 1); + } +}; + +/** + * @param {*} obj Object to test. + * @return {boolean} Whether given object is array-like or iterable. + * @private + */ +goog.testing.asserts.isArrayLikeOrIterable_ = function(obj) { + return goog.isArrayLike(obj) || goog.testing.asserts.isIterable_(obj); +}; + +/** + * @param {*} a The value to assert (1 arg) or debug message (2 args). + * @param {*=} opt_b The value to assert (2 args only). + */ +var assertEvaluatesToTrue = + goog.testing.asserts.assertEvaluatesToTrue = function(a, opt_b) { + _validateArguments(1, arguments); + var value = nonCommentArg(1, 1, arguments); + if (!value) { + _assert( + commentArg(1, arguments), false, 'Expected to evaluate to true'); + } + }; + + +/** + * @param {*} a The value to assert (1 arg) or debug message (2 args). + * @param {*=} opt_b The value to assert (2 args only). + */ +var assertEvaluatesToFalse = + goog.testing.asserts.assertEvaluatesToFalse = function(a, opt_b) { + _validateArguments(1, arguments); + var value = nonCommentArg(1, 1, arguments); + if (value) { + _assert( + commentArg(1, arguments), false, 'Expected to evaluate to false'); + } + }; + + +/** + * Compares two HTML snippets. + * + * Take extra care if attributes are involved. `assertHTMLEquals`'s + * implementation isn't prepared for complex cases. For example, the following + * comparisons erroneously fail: + *
+ * assertHTMLEquals('', '');
+ * assertHTMLEquals('
', '
'); + * assertHTMLEquals('', ''); + *
+ * + * When in doubt, use `goog.testing.dom.assertHtmlMatches`. + * + * @param {*} a The expected value (2 args) or the debug message (3 args). + * @param {*} b The actual value (2 args) or the expected value (3 args). + * @param {*=} opt_c The actual value (3 args only). + */ +var assertHTMLEquals = goog.testing.asserts.assertHTMLEquals = function( + a, b, opt_c) { + _validateArguments(2, arguments); + var var1 = nonCommentArg(1, 2, arguments); + var var2 = nonCommentArg(2, 2, arguments); + var var1Standardized = standardizeHTML(var1); + var var2Standardized = standardizeHTML(var2); + + _assert( + commentArg(2, arguments), var1Standardized === var2Standardized, + goog.testing.asserts.getDefaultErrorMsg_( + var1Standardized, var2Standardized)); +}; + + +/** + * Compares two CSS property values to make sure that they represent the same + * things. This will normalize values in the browser. For example, in Firefox, + * this assertion will consider "rgb(0, 0, 255)" and "#0000ff" to be identical + * values for the "color" property. This function won't normalize everything -- + * for example, in most browsers, "blue" will not match "#0000ff". It is + * intended only to compensate for unexpected normalizations performed by + * the browser that should also affect your expected value. + * @param {string} a Assertion message, or the CSS property name. + * @param {string} b CSS property name, or the expected value. + * @param {string} c The expected value, or the actual value. + * @param {string=} opt_d The actual value. + */ +var assertCSSValueEquals = goog.testing.asserts.assertCSSValueEquals = function( + a, b, c, opt_d) { + _validateArguments(3, arguments); + var propertyName = nonCommentArg(1, 3, arguments); + var expectedValue = nonCommentArg(2, 3, arguments); + var actualValue = nonCommentArg(3, 3, arguments); + var expectedValueStandardized = + standardizeCSSValue(propertyName, expectedValue); + var actualValueStandardized = standardizeCSSValue(propertyName, actualValue); + + _assert( + commentArg(3, arguments), + expectedValueStandardized == actualValueStandardized, + goog.testing.asserts.getDefaultErrorMsg_( + expectedValueStandardized, actualValueStandardized)); +}; + + +/** + * @param {*} a The expected value (2 args) or the debug message (3 args). + * @param {*} b The actual value (2 args) or the expected value (3 args). + * @param {*=} opt_c The actual value (3 args only). + */ +var assertHashEquals = goog.testing.asserts.assertHashEquals = function( + a, b, opt_c) { + _validateArguments(2, arguments); + var var1 = nonCommentArg(1, 2, arguments); + var var2 = nonCommentArg(2, 2, arguments); + var message = commentArg(2, arguments); + for (var key in var1) { + _assert( + message, key in var2, + 'Expected hash had key ' + key + ' that was not found'); + _assert( + message, var1[key] == var2[key], 'Value for key ' + key + + ' mismatch - expected = ' + var1[key] + ', actual = ' + var2[key]); + } + + for (var key in var2) { + _assert( + message, key in var1, + 'Actual hash had key ' + key + ' that was not expected'); + } +}; + + +/** + * @param {*} a The expected value (3 args) or the debug message (4 args). + * @param {*} b The actual value (3 args) or the expected value (4 args). + * @param {*} c The tolerance (3 args) or the actual value (4 args). + * @param {*=} opt_d The tolerance (4 args only). + */ +var assertRoughlyEquals = goog.testing.asserts.assertRoughlyEquals = function( + a, b, c, opt_d) { + _validateArguments(3, arguments); + var expected = nonCommentArg(1, 3, arguments); + var actual = nonCommentArg(2, 3, arguments); + var tolerance = nonCommentArg(3, 3, arguments); + _assert( + commentArg(3, arguments), + goog.testing.asserts.numberRoughEqualityPredicate_( + expected, actual, tolerance), + 'Expected ' + expected + ', but got ' + actual + ' which was more than ' + + tolerance + ' away'); +}; + + +/** + * Checks if the test value is included in the given container. The container + * can be a string (where "included" means a substring), an array or any + * `IArrayLike` (where "included" means a member), or any type implementing + * `indexOf` with similar semantics (returning -1 for not included). + * + * @param {*} a Failure message (3 arguments) or the test value + * (2 arguments). + * @param {*} b The test value (3 arguments) or the container + * (2 arguments). + * @param {*=} opt_c The container. + */ +goog.testing.asserts.assertContains = function(a, b, opt_c) { + _validateArguments(2, arguments); + var contained = nonCommentArg(1, 2, arguments); + var container = nonCommentArg(2, 2, arguments); + _assert( + commentArg(2, arguments), + goog.testing.asserts.contains_(container, contained), + 'Expected \'' + container + '\' to contain \'' + contained + '\''); +}; +/** @const */ +var assertContains = goog.testing.asserts.assertContains; + +/** + * Checks if the test value is not included in the given container. The + * container can be a string (where "included" means a substring), an array or + * any `IArrayLike` (where "included" means a member), or any type implementing + * `indexOf` with similar semantics (returning -1 for not included). + * @param {*} a Failure message (3 arguments) or the contained element + * (2 arguments). + * @param {*} b The contained element (3 arguments) or the container + * (2 arguments). + * @param {*=} opt_c The container. + */ +var assertNotContains = goog.testing.asserts.assertNotContains = function( + a, b, opt_c) { + _validateArguments(2, arguments); + var contained = nonCommentArg(1, 2, arguments); + var container = nonCommentArg(2, 2, arguments); + _assert( + commentArg(2, arguments), + !goog.testing.asserts.contains_(container, contained), + 'Expected \'' + container + '\' not to contain \'' + contained + '\''); +}; + + +/** + * Checks if the given string matches the given regular expression. + * @param {*} a Failure message (3 arguments) or the expected regular + * expression as a string or RegExp (2 arguments). + * @param {*} b The regular expression (3 arguments) or the string to test + * (2 arguments). + * @param {*=} opt_c The string to test. + */ +var assertRegExp = goog.testing.asserts.assertRegExp = function(a, b, opt_c) { + _validateArguments(2, arguments); + var regexp = nonCommentArg(1, 2, arguments); + var string = nonCommentArg(2, 2, arguments); + if (typeof(regexp) == 'string') { + regexp = new RegExp(regexp); + } + _assert( + commentArg(2, arguments), regexp.test(string), + 'Expected \'' + string + '\' to match RegExp ' + regexp.toString()); +}; + + +/** + * Converts an array-like or iterable object to an array (clones it if it's + * already an array). + * @param {!Iterable|!IArrayLike} obj The collection object. + * @return {!Array} Copy of the collection as array. + * @private + */ +goog.testing.asserts.toArray_ = function(obj) { + var ret = []; + if (goog.testing.asserts.isIterable_(obj)) { + var iterator = + goog.testing.asserts.getIterator_(/** @type {!Iterable} */ (obj)); + + // Cannot use for..of syntax here as ES6 syntax is not available in Closure. + // See b/117231092 + while (true) { + var result = iterator.next(); + if (result.done) { + return ret; + } + ret.push(result.value); + } + } + + for (var i = 0; i < obj.length; i++) { + ret[i] = obj[i]; + } + return ret; +}; + +// TODO(nnaze): Consider moving isIterable_ and getIterator_ functionality +// into goog.iter.es6. See discussion in cl/217356297. + +/** + * @param {*} obj + * @return {boolean} Whether the object is iterable (JS iterator protocol). + * @private + */ +goog.testing.asserts.isIterable_ = function(obj) { + return !!( + typeof Symbol !== 'undefined' && Symbol.iterator && obj[Symbol.iterator]); +}; + +/** + * @param {!Iterable} iterable + * @return {!Iterator} An iterator for obj. + * @throws {!goog.testing.JsUnitException} If the given object is not iterable. + * @private + */ +goog.testing.asserts.getIterator_ = function(iterable) { + if (!goog.testing.asserts.isIterable_(iterable)) { + goog.testing.asserts.raiseException('parameter iterable is not iterable'); + } + + return iterable[Symbol.iterator](); +}; + + +/** + * Finds the position of the first occurrence of an element in a container. + * @param {IArrayLike|{indexOf: function(*): number}} container + * The array to find the element in. + * @param {*} contained Element to find. + * @return {number} Index of the first occurrence or -1 if not found. + * @private + */ +goog.testing.asserts.indexOf_ = function(container, contained) { + if (typeof container.indexOf == 'function') { + return container.indexOf(contained); + } else { + // IE6/7 do not have indexOf so do a search. + for (var i = 0; i < container.length; i++) { + if (container[i] === contained) { + return i; + } + } + return -1; + } +}; + + +/** + * Tells whether the array contains the given element. + * @param {IArrayLike|{indexOf: function(*): number}} container The array to + * find the element in. + * @param {*} contained Element to find. + * @return {boolean} Whether the element is in the array. + * @private + */ +goog.testing.asserts.contains_ = function(container, contained) { + // TODO(user): Can we check for container.contains as well? + // That would give us support for most goog.structs (though weird results + // with anything else with a contains method, like goog.math.Range). Falling + // back with container.some would catch all iterables, too. + return goog.testing.asserts.indexOf_(container, contained) != -1; +}; + +var standardizeHTML = function(html) { + var translator = document.createElement('DIV'); + translator.innerHTML = html; + + // Trim whitespace from result (without relying on goog.string) + return translator.innerHTML.replace(/^\s+|\s+$/g, ''); +}; + + +/** + * Standardizes a CSS value for a given property by applying it to an element + * and then reading it back. + * @param {string} propertyName CSS property name. + * @param {string} value CSS value. + * @return {string} Normalized CSS value. + */ +var standardizeCSSValue = function(propertyName, value) { + var styleDeclaration = document.createElement('DIV').style; + styleDeclaration[propertyName] = value; + return styleDeclaration[propertyName]; +}; + + +/** + * Raises a JsUnit exception with the given comment. If the exception is + * unexpectedly caught during a unit test, it will be rethrown so that it is + * seen by the test framework. + * @param {string} comment A summary for the exception. + * @param {string=} opt_message A description of the exception. + */ +goog.testing.asserts.raiseException = function(comment, opt_message) { + var e = new goog.testing.JsUnitException(comment, opt_message); + + var testCase = _getCurrentTestCase(); + if (testCase) { + testCase.raiseAssertionException(e); + } else { + goog.global.console.error( + 'Failed to save thrown exception: no test case is installed.'); + throw e; + } +}; + + +/** + * Helper function for assertObjectEquals. + * @param {string} prop A property name. + * @return {boolean} If the property name is an array index. + * @private + */ +goog.testing.asserts.isArrayIndexProp_ = function(prop) { + return (Number(prop) | 0) == prop; +}; + +/** @define {boolean} */ +goog.define('goog.EXPORT_ASSERTIONS', true); +/* + * These symbols are both exported in the global namespace (for legacy + * reasons) and as part of the goog.testing.asserts namespace. Although they + * can be used globally in tests, these symbols are allowed to be imported for + * cleaner typing. + */ +if (goog.EXPORT_ASSERTIONS) { + goog.exportSymbol('fail', fail); + goog.exportSymbol('assert', assert); + goog.exportSymbol('assertThrows', assertThrows); + goog.exportSymbol('assertNotThrows', assertNotThrows); + goog.exportSymbol('assertThrowsJsUnitException', assertThrowsJsUnitException); + goog.exportSymbol('assertRejects', assertRejects); + goog.exportSymbol('assertTrue', assertTrue); + goog.exportSymbol('assertFalse', assertFalse); + goog.exportSymbol('assertEquals', assertEquals); + goog.exportSymbol('assertNotEquals', assertNotEquals); + goog.exportSymbol('assertNull', assertNull); + goog.exportSymbol('assertNotNull', assertNotNull); + goog.exportSymbol('assertUndefined', assertUndefined); + goog.exportSymbol('assertNotUndefined', assertNotUndefined); + goog.exportSymbol('assertNullOrUndefined', assertNullOrUndefined); + goog.exportSymbol('assertNotNullNorUndefined', assertNotNullNorUndefined); + goog.exportSymbol('assertNonEmptyString', assertNonEmptyString); + goog.exportSymbol('assertNaN', assertNaN); + goog.exportSymbol('assertNotNaN', assertNotNaN); + goog.exportSymbol('assertObjectEquals', assertObjectEquals); + goog.exportSymbol('assertObjectRoughlyEquals', assertObjectRoughlyEquals); + goog.exportSymbol('assertObjectNotEquals', assertObjectNotEquals); + goog.exportSymbol('assertArrayEquals', assertArrayEquals); + goog.exportSymbol('assertElementsEquals', assertElementsEquals); + goog.exportSymbol('assertElementsRoughlyEqual', assertElementsRoughlyEqual); + goog.exportSymbol('assertSameElements', assertSameElements); + goog.exportSymbol('assertEvaluatesToTrue', assertEvaluatesToTrue); + goog.exportSymbol('assertEvaluatesToFalse', assertEvaluatesToFalse); + goog.exportSymbol('assertHTMLEquals', assertHTMLEquals); + goog.exportSymbol('assertHashEquals', assertHashEquals); + goog.exportSymbol('assertRoughlyEquals', assertRoughlyEquals); + goog.exportSymbol('assertContains', assertContains); + goog.exportSymbol('assertNotContains', assertNotContains); + goog.exportSymbol('assertRegExp', assertRegExp); +} diff --git a/closure-library/closure/goog/testing/async/mockcontrol.js b/closure-library/closure/goog/testing/async/mockcontrol.js new file mode 100644 index 0000000000..58864c9014 --- /dev/null +++ b/closure-library/closure/goog/testing/async/mockcontrol.js @@ -0,0 +1,173 @@ +// Copyright 2010 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A wrapper for MockControl that provides mocks and assertions + * for testing asynchronous code. All assertions will only be verified when + * $verifyAll is called on the wrapped MockControl. + * + * This class is meant primarily for testing code that exposes asynchronous APIs + * without being truly asynchronous (using asynchronous primitives like browser + * events or timeouts). This is often the case when true asynchronous + * depedencies have been mocked out. This means that it doesn't rely on + * AsyncTestCase or DeferredTestCase, although it can be used with those as + * well. + * + * Example usage: + * + *
+ * var mockControl = new goog.testing.MockControl();
+ * var asyncMockControl = new goog.testing.async.MockControl(mockControl);
+ *
+ * myAsyncObject.onSuccess(asyncMockControl.asyncAssertEquals(
+ *     'callback should run and pass the correct value',
+ *     'http://someurl.com');
+ * asyncMockControl.assertDeferredEquals(
+ *     'deferred object should be resolved with the correct value',
+ *     'http://someurl.com',
+ *     myAsyncObject.getDeferredUrl());
+ * asyncMockControl.run();
+ * mockControl.$verifyAll();
+ * 
+ * + */ + + +goog.setTestOnly('goog.testing.async.MockControl'); +goog.provide('goog.testing.async.MockControl'); + +goog.require('goog.asserts'); +goog.require('goog.async.Deferred'); +goog.require('goog.debug'); +goog.require('goog.testing.MockControl'); +goog.require('goog.testing.asserts'); +goog.require('goog.testing.mockmatchers.IgnoreArgument'); + +/** + * Provides asynchronous mocks and assertions controlled by a parent + * MockControl. + * + * @param {goog.testing.MockControl} mockControl The parent MockControl. + * @constructor + * @final + */ +goog.testing.async.MockControl = function(mockControl) { + /** + * The parent MockControl. + * @type {goog.testing.MockControl} + * @private + */ + this.mockControl_ = mockControl; +}; + + +/** + * Returns a function that will assert that it will be called, and run the given + * callback when it is. + * + * @template THIS + * @param {string} name The name of the callback mock. + * @param {function(this:THIS, ...*) : *} callback The wrapped callback. This will be + * called when the returned function is called. + * @param {THIS=} opt_selfObj The object which this should point to when the + * callback is run. + * @return {!Function} The mock callback. + * @suppress {missingProperties} Mocks do not fit in the type system well. + */ +goog.testing.async.MockControl.prototype.createCallbackMock = function( + name, callback, opt_selfObj) { + goog.asserts.assert( + goog.isString(name), + 'name parameter ' + goog.debug.deepExpose(name) + ' should be a string'); + + var ignored = new goog.testing.mockmatchers.IgnoreArgument(); + + // Use everyone's favorite "double-cast" trick to subvert the type system. + var mock = this.mockControl_.createFunctionMock(name); + var mockAsFn = /** @type {Function} */ (/** @type {*} */ (mock)); + + mockAsFn(ignored).$does(function(args) { + return callback.apply(opt_selfObj || /** @type {?} */ (this), args); + }); + mock.$replay(); + return function() { + return mockAsFn(arguments); + }; +}; + + +/** + * Returns a function that will assert that its arguments are equal to the + * arguments given to asyncAssertEquals. In addition, the function also asserts + * that it will be called. + * + * @param {string} message A message to print if the arguments are wrong. + * @param {...*} var_args The arguments to assert. + * @return {function(...*) : void} The mock callback. + */ +goog.testing.async.MockControl.prototype.asyncAssertEquals = function( + message, var_args) { + var expectedArgs = Array.prototype.slice.call(arguments, 1); + return this.createCallbackMock('asyncAssertEquals', function() { + assertObjectEquals( + message, expectedArgs, Array.prototype.slice.call(arguments)); + }); +}; + + +/** + * Asserts that a deferred object will have an error and call its errback + * function. + * @param {goog.async.Deferred} deferred The deferred object. + * @param {function() : void} fn A function wrapping the code in which the error + * will occur. + */ +goog.testing.async.MockControl.prototype.assertDeferredError = function( + deferred, fn) { + deferred.addErrback( + this.createCallbackMock('assertDeferredError', function() {})); + fn(); +}; + + +/** + * Asserts that a deferred object will call its callback with the given value. + * + * @param {string} message A message to print if the arguments are wrong. + * @param {goog.async.Deferred|*} expected The expected value. If this is a + * deferred object, then the expected value is the deferred value. + * @param {goog.async.Deferred|*} actual The actual value. If this is a deferred + * object, then the actual value is the deferred value. Either this or + * 'expected' must be deferred. + */ +goog.testing.async.MockControl.prototype.assertDeferredEquals = function( + message, expected, actual) { + if (expected instanceof goog.async.Deferred) { + // Assert that the first deferred is resolved. + expected.addCallback( + this.createCallbackMock('assertDeferredEquals', function(exp) { + // Assert that the second deferred is resolved, and that the value is + // as expected. + if (actual instanceof goog.async.Deferred) { + actual.addCallback(this.asyncAssertEquals(message, exp)); + } else { + assertObjectEquals(message, exp, actual); + } + }, this)); + } else if (actual instanceof goog.async.Deferred) { + actual.addCallback(this.asyncAssertEquals(message, expected)); + } else { + throw new Error('Either expected or actual must be deferred'); + } +}; diff --git a/closure-library/closure/goog/testing/asynctestcase.js b/closure-library/closure/goog/testing/asynctestcase.js new file mode 100644 index 0000000000..8864d33503 --- /dev/null +++ b/closure-library/closure/goog/testing/asynctestcase.js @@ -0,0 +1,915 @@ +// Copyright 2010 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// All Rights Reserved. + +/** + * @fileoverview A class representing a set of test functions that use + * asynchronous functions that cannot be meaningfully mocked. + * + * To create a Google-compatible JsUnit test using this test case, put the + * following snippet in your test: + * + * var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall(); + * + * To make the test runner wait for your asynchronous behaviour, use: + * + * asyncTestCase.waitForAsync('Waiting for xhr to respond'); + * + * The next test will not start until the following call is made, or a + * timeout occurs: + * + * asyncTestCase.continueTesting(); + * + * There does NOT need to be a 1:1 mapping of waitForAsync calls and + * continueTesting calls. The next test will be run after a single call to + * continueTesting is made, as long as there is no subsequent call to + * waitForAsync in the same thread. + * + * Example: + * // Returning here would cause the next test to be run. + * asyncTestCase.waitForAsync('description 1'); + * // Returning here would *not* cause the next test to be run. + * // Only effect of additional waitForAsync() calls is an updated + * // description in the case of a timeout. + * asyncTestCase.waitForAsync('updated description'); + * asyncTestCase.continueTesting(); + * // Returning here would cause the next test to be run. + * asyncTestCase.waitForAsync('just kidding, still running.'); + * // Returning here would *not* cause the next test to be run. + * + * The test runner can also be made to wait for more than one asynchronous + * event with: + * + * asyncTestCase.waitForSignals(n); + * + * The next test will not start until asyncTestCase.signal() is called n times, + * or the test step timeout is exceeded. + * + * This class supports asynchronous behaviour in all test functions except for + * tearDownPage. If such support is needed, it can be added. + * + * Example Usage: + * + * var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall(); + * // Optionally, set a longer-than-normal step timeout. + * asyncTestCase.stepTimeout = 30 * 1000; + * + * function testSetTimeout() { + * var step = 0; + * function stepCallback() { + * step++; + * switch (step) { + * case 1: + * var startTime = goog.now(); + * asyncTestCase.waitForAsync('step 1'); + * window.setTimeout(stepCallback, 100); + * break; + * case 2: + * assertTrue('Timeout fired too soon', + * goog.now() - startTime >= 100); + * asyncTestCase.waitForAsync('step 2'); + * window.setTimeout(stepCallback, 100); + * break; + * case 3: + * assertTrue('Timeout fired too soon', + * goog.now() - startTime >= 200); + * asyncTestCase.continueTesting(); + * break; + * default: + * fail('Unexpected call to stepCallback'); + * } + * } + * stepCallback(); + * } + * + * Known Issues: + * IE7 Exceptions: + * As the failingtest.html will show, it appears as though ie7 does not + * propagate an exception past a function called using the func.call() + * syntax. This causes case 3 of the failing tests (exceptions) to show up + * as timeouts in IE. + * window.onerror: + * This seems to catch errors only in ff2/ff3. It does not work in Safari or + * IE7. The consequence of this is that exceptions that would have been + * caught by window.onerror show up as timeouts. + * + * @author agrieve@google.com (Andrew Grieve) + */ + +goog.setTestOnly('goog.testing.AsyncTestCase'); +goog.provide('goog.testing.AsyncTestCase'); +goog.provide('goog.testing.AsyncTestCase.ControlBreakingException'); + +goog.require('goog.asserts'); +goog.require('goog.testing.TestCase'); +goog.require('goog.testing.asserts'); + + + +/** + * A test case that is capable of running tests that contain asynchronous logic. + * @param {string=} opt_name A descriptive name for the test case. + * @extends {goog.testing.TestCase} + * @constructor + * @deprecated Use goog.testing.TestCase instead. goog.testing.TestCase now + * supports async testing using promises. + */ +goog.testing.AsyncTestCase = function(opt_name) { + goog.testing.TestCase.call(this, opt_name); +}; +goog.inherits(goog.testing.AsyncTestCase, goog.testing.TestCase); + + +/** + * Represents result of top stack function call. + * @typedef {{controlBreakingExceptionThrown: boolean, message: string}} + * @private + */ +goog.testing.AsyncTestCase.TopStackFuncResult_; + + + +/** + * An exception class used solely for control flow. + * @param {string=} opt_message Error message. + * @constructor + * @extends {Error} + * @final + */ +goog.testing.AsyncTestCase.ControlBreakingException = function(opt_message) { + goog.testing.AsyncTestCase.ControlBreakingException.base( + this, 'constructor', opt_message); + + /** + * The exception message. + * @type {string} + */ + this.message = opt_message || ''; +}; +goog.inherits(goog.testing.AsyncTestCase.ControlBreakingException, Error); + + +/** + * Return value for .toString(). + * @type {string} + */ +goog.testing.AsyncTestCase.ControlBreakingException.TO_STRING = + '[AsyncTestCase.ControlBreakingException]'; + + +/** + * Marks this object as a ControlBreakingException + * @type {boolean} + */ +goog.testing.AsyncTestCase.ControlBreakingException.prototype + .isControlBreakingException = true; + + +/** @override */ +goog.testing.AsyncTestCase.ControlBreakingException.prototype.toString = + function() { + // This shows up in the console when the exception is not caught. + return goog.testing.AsyncTestCase.ControlBreakingException.TO_STRING; +}; + + +/** + * How long to wait for a single step of a test to complete in milliseconds. + * A step starts when a call to waitForAsync() is made. + * @type {number} + */ +goog.testing.AsyncTestCase.prototype.stepTimeout = 1000; + + +/** + * How long to wait after a failed test before moving onto the next one. + * The purpose of this is to allow any pending async callbacks from the failing + * test to finish up and not cause the next test to fail. + * @type {number} + */ +goog.testing.AsyncTestCase.prototype.timeToSleepAfterFailure = 500; + + +/** + * Turn on extra logging to help debug failing async. tests. + * @type {boolean} + * @private + */ +goog.testing.AsyncTestCase.prototype.enableDebugLogs_ = false; + + +/** + * A reference to the original asserts.js assert_() function. + * @private {?function(?, ?, ?):?} + */ +goog.testing.AsyncTestCase.prototype.origAssert_; + + +/** + * A reference to the original asserts.js fail() function. + * @private {?function(?)} + */ +goog.testing.AsyncTestCase.prototype.origFail_ = null; + + +/** + * A reference to the original window.onerror function. + * @type {?Function|undefined} + * @private + */ +goog.testing.AsyncTestCase.prototype.origOnError_; + + +/** + * The stage of the test we are currently on. + * @type {?Function|undefined}} + * @private + */ +goog.testing.AsyncTestCase.prototype.curStepFunc_; + + +/** + * The name of the stage of the test we are currently on. + * @type {string} + * @private + */ +goog.testing.AsyncTestCase.prototype.curStepName_ = ''; + + +/** + * The stage of the test we should run next. + * @type {?function(this:goog.testing.AsyncTestCase, ...?):?} + * @private + */ +goog.testing.AsyncTestCase.prototype.nextStepFunc_ = null; + + +/** + * The name of the stage of the test we should run next. + * @type {string} + * @private + */ +goog.testing.AsyncTestCase.prototype.nextStepName_ = ''; + + +/** + * The handle to the current setTimeout timer. + * @type {number} + * @private + */ +goog.testing.AsyncTestCase.prototype.timeoutHandle_ = 0; + + +/** + * Marks if the cleanUp() function has been called for the currently running + * test. + * @type {boolean} + * @private + */ +goog.testing.AsyncTestCase.prototype.cleanedUp_ = false; + + +/** + * The currently active test. + * @type {goog.testing.TestCase.Test|undefined} + * @protected + */ +goog.testing.AsyncTestCase.prototype.activeTest; + + +/** + * A flag to prevent recursive exception handling. + * @type {boolean} + * @private + */ +goog.testing.AsyncTestCase.prototype.inException_ = false; + + +/** + * Flag used to determine if we can move to the next step in the testing loop. + * @type {boolean} + * @private + */ +goog.testing.AsyncTestCase.prototype.isReady_ = true; + + +/** + * Number of signals to wait for before continuing testing when waitForSignals + * is used. + * @type {number} + * @private + */ +goog.testing.AsyncTestCase.prototype.expectedSignalCount_ = 0; + + +/** + * Number of signals received. + * @type {number} + * @private + */ +goog.testing.AsyncTestCase.prototype.receivedSignalCount_ = 0; + + +/** + * Flag that tells us if there is a function in the call stack that will make + * a call to pump_(). + * @type {boolean} + * @private + */ +goog.testing.AsyncTestCase.prototype.returnWillPump_ = false; + + +/** + * The number of times we have thrown a ControlBreakingException so that we + * know not to complain in our window.onerror handler. In Webkit, window.onerror + * is not supported, and so this counter will keep going up but we won't care + * about it. + * @type {number} + * @private + */ +goog.testing.AsyncTestCase.prototype.numControlExceptionsExpected_ = 0; + + +/** + * The current step name. + * @return {string} Step name. + * @protected + */ +goog.testing.AsyncTestCase.prototype.getCurrentStepName = function() { + return this.curStepName_; +}; + + +/** + * Preferred way of creating an AsyncTestCase. Creates one and initializes it + * with the G_testRunner. + * @param {string=} opt_name A descriptive name for the test case. + * @return {!goog.testing.AsyncTestCase} The created AsyncTestCase. + */ +goog.testing.AsyncTestCase.createAndInstall = function(opt_name) { + var asyncTestCase = new goog.testing.AsyncTestCase(opt_name); + goog.testing.TestCase.initializeTestRunner(asyncTestCase); + return asyncTestCase; +}; + + +/** + * Informs the testcase not to continue to the next step in the test cycle + * until continueTesting is called. + * @param {string=} opt_name A description of what we are waiting for. + */ +goog.testing.AsyncTestCase.prototype.waitForAsync = function(opt_name) { + this.isReady_ = false; + this.curStepName_ = opt_name || this.curStepName_; + + // Reset the timer that tracks if the async test takes too long. + this.stopTimeoutTimer_(); + this.startTimeoutTimer_(); +}; + + +/** + * Continue with the next step in the test cycle. + */ +goog.testing.AsyncTestCase.prototype.continueTesting = function() { + if (this.receivedSignalCount_ < this.expectedSignalCount_) { + var remaining = this.expectedSignalCount_ - this.receivedSignalCount_; + throw new Error('Still waiting for ' + remaining + ' signals.'); + } + this.endCurrentStep_(); +}; + + +/** + * Ends the current test step and queues the next test step to run. + * @private + */ +goog.testing.AsyncTestCase.prototype.endCurrentStep_ = function() { + if (!this.isReady_) { + // We are a potential entry point, so we pump. + this.isReady_ = true; + this.stopTimeoutTimer_(); + // Run this in a setTimeout so that the caller has a chance to call + // waitForAsync() again before we continue. + this.timeout(goog.bind(this.pump_, this, null), 0); + } +}; + + +/** + * Informs the testcase not to continue to the next step in the test cycle + * until signal is called the specified number of times. Within a test, this + * function behaves additively if called multiple times; the number of signals + * to wait for will be the sum of all expected number of signals this function + * was called with. + * @param {number} times The number of signals to receive before + * continuing testing. + * @param {string=} opt_name A description of what we are waiting for. + */ +goog.testing.AsyncTestCase.prototype.waitForSignals = function( + times, opt_name) { + this.expectedSignalCount_ += times; + if (this.receivedSignalCount_ < this.expectedSignalCount_) { + this.waitForAsync(opt_name); + } +}; + + +/** + * Signals once to continue with the test. If this is the last signal that the + * test was waiting on, call continueTesting. + */ +goog.testing.AsyncTestCase.prototype.signal = function() { + if (++this.receivedSignalCount_ === this.expectedSignalCount_ && + this.expectedSignalCount_ > 0) { + this.endCurrentStep_(); + } +}; + + +/** + * Handles an exception thrown by a test. + * @param {?=} opt_e The exception object associated with the failure + * or a string. + * @throws Always throws a ControlBreakingException. + */ +goog.testing.AsyncTestCase.prototype.doAsyncError = function(opt_e) { + // If we've caught an exception that we threw, then just pass it along. This + // can happen if doAsyncError() was called from a call to assert and then + // again by pump_(). + if (opt_e && opt_e.isControlBreakingException) { + throw opt_e; + } + + // Prevent another timeout error from triggering for this test step. + this.stopTimeoutTimer_(); + + // doError() uses test.name. Here, we create a dummy test and give it a more + // helpful name based on the step we're currently on. + var fakeTestObj = + new goog.testing.TestCase.Test(this.curStepName_, goog.nullFunction); + if (this.activeTest) { + fakeTestObj.name = this.activeTest.name + ' [' + fakeTestObj.name + ']'; + } + + if (this.activeTest) { + // Log the error, then fail the test. + this.recordError(fakeTestObj.name, opt_e); + this.doError(fakeTestObj); + } else { + this.exceptionBeforeTest = opt_e; + } + + // This is a potential entry point, so we pump. We also add in a bit of a + // delay to try and prevent any async behavior from the failed test from + // causing the next test to fail. + this.timeout( + goog.bind(this.pump_, this, this.doAsyncErrorTearDown_), + this.timeToSleepAfterFailure); + + // We just caught an exception, so we do not want the code above us on the + // stack to continue executing. If pump_ is in our call-stack, then it will + // batch together multiple errors, so we only increment the count if pump_ is + // not in the stack and let pump_ increment the count when it batches them. + if (!this.returnWillPump_) { + this.numControlExceptionsExpected_ += 1; + this.dbgLog_( + 'doAsynError: numControlExceptionsExpected_ = ' + + this.numControlExceptionsExpected_ + ' and throwing exception.'); + } + + // Copy the error message to ControlBreakingException. + var message = ''; + if (typeof opt_e == 'string') { + message = opt_e; + } else if (opt_e && opt_e.message) { + message = opt_e.message; + } + throw new goog.testing.AsyncTestCase.ControlBreakingException(message); +}; + + +/** + * Sets up the test page and then waits until the test case has been marked + * as ready before executing the tests. + * @override + */ +goog.testing.AsyncTestCase.prototype.runTests = function() { + this.hookAssert_(); + this.hookOnError_(); + + goog.testing.TestCase.currentTestName = null; + this.setNextStep_(this.doSetUpPage_, 'setUpPage'); + // We are an entry point, so we pump. + this.pump_(); +}; + + +/** + * Starts the tests. + * @override + */ +goog.testing.AsyncTestCase.prototype.cycleTests = function() { + // We are an entry point, so we pump. + this.saveMessage('Start'); + this.setNextStep_(this.doIteration_, 'doIteration'); + this.pump_(); +}; + + +/** + * Finalizes the test case, called when the tests have finished executing. + * @override + */ +goog.testing.AsyncTestCase.prototype.finalize = function() { + this.unhookAll_(); + this.setNextStep_(null, 'finalized'); + goog.testing.AsyncTestCase.superClass_.finalize.call(this); +}; + + +/** + * Enables verbose logging of what is happening inside of the AsyncTestCase. + */ +goog.testing.AsyncTestCase.prototype.enableDebugLogging = function() { + this.enableDebugLogs_ = true; +}; + + +/** + * Logs the given debug message to the console (when enabled). + * @param {string} message The message to log. + * @private + */ +goog.testing.AsyncTestCase.prototype.dbgLog_ = function(message) { + if (this.enableDebugLogs_) { + this.log('AsyncTestCase - ' + message); + } +}; + + +/** + * Wraps doAsyncError() for when we are sure that the test runner has no user + * code above it in the stack. + * @param {string|Error=} opt_e The exception object associated with the + * failure or a string. + * @private + */ +goog.testing.AsyncTestCase.prototype.doTopOfStackAsyncError_ = function(opt_e) { + + try { + this.doAsyncError(opt_e); + } catch (e) { + // We know that we are on the top of the stack, so there is no need to + // throw this exception in this case. + if (e.isControlBreakingException) { + this.numControlExceptionsExpected_ -= 1; + this.dbgLog_( + 'doTopOfStackAsyncError_: numControlExceptionsExpected_ = ' + + this.numControlExceptionsExpected_ + ' and catching exception.'); + } else { + throw e; + } + } +}; + + +/** + * Calls the tearDown function, catching any errors, and then moves on to + * the next step in the testing cycle. + * @private + */ +goog.testing.AsyncTestCase.prototype.doAsyncErrorTearDown_ = function() { + if (this.inException_) { + // We get here if tearDown is throwing the error. + // Upon calling continueTesting, the inline function 'doAsyncError' (set + // below) is run. + this.endCurrentStep_(); + } else { + this.inException_ = true; + this.isReady_ = true; + + // The continue point is different depending on if the error happened in + // setUpPage() or in setUp()/test*()/tearDown(). + var stepFuncAfterError = this.nextStepFunc_; + var stepNameAfterError = 'TestCase.execute (after error)'; + if (this.activeTest) { + stepFuncAfterError = this.doIteration_; + stepNameAfterError = 'doIteration (after error)'; + } + + // We must set the next step before calling tearDown. + this.setNextStep_(function() { + this.inException_ = false; + // This is null when an error happens in setUpPage. + this.setNextStep_(stepFuncAfterError, stepNameAfterError); + }, 'doAsyncError'); + + // Call the test's tearDown(). + if (!this.cleanedUp_) { + this.cleanedUp_ = true; + this.tearDown(); + } + } +}; + + +/** + * Replaces the asserts.js assert_() and fail() functions with a wrappers to + * catch the exceptions. + * @private + */ +goog.testing.AsyncTestCase.prototype.hookAssert_ = function() { + if (!this.origAssert_) { + this.origAssert_ = _assert; + this.origFail_ = fail; + var self = this; + + _assert = function() { + var expectedUnknownThis = /** @type {?} */ (this); + try { + self.origAssert_.apply(expectedUnknownThis, arguments); + } catch (e) { + self.dbgLog_('Wrapping failed assert()'); + self.doAsyncError(e); + } + }; + + fail = function() { + var expectedUnknownThis = /** @type {?} */ (this); + try { + self.origFail_.apply(expectedUnknownThis, arguments); + } catch (e) { + self.dbgLog_('Wrapping fail()'); + self.doAsyncError(e); + } + }; + } +}; + + +/** + * Sets a window.onerror handler for catching exceptions that happen in async + * callbacks. Note that as of Safari 3.1, Safari does not support this. + * @private + */ +goog.testing.AsyncTestCase.prototype.hookOnError_ = function() { + if (!this.origOnError_) { + this.origOnError_ = window.onerror; + var self = this; + window.onerror = function(error, url, line) { + // Ignore exceptions that we threw on purpose. + var cbe = goog.testing.AsyncTestCase.ControlBreakingException.TO_STRING; + if (String(error).indexOf(cbe) != -1 && + self.numControlExceptionsExpected_) { + self.numControlExceptionsExpected_ -= 1; + self.dbgLog_( + 'window.onerror: numControlExceptionsExpected_ = ' + + self.numControlExceptionsExpected_ + ' and ignoring exception. ' + + error); + // Tell the browser not to compain about the error. + return true; + } else { + self.dbgLog_('window.onerror caught exception.'); + var message = error + '\nURL: ' + url + '\nLine: ' + line; + self.doTopOfStackAsyncError_(message); + // Tell the browser to complain about the error. + return false; + } + }; + } +}; + + +/** + * Unhooks window.onerror and _assert. + * @private + */ +goog.testing.AsyncTestCase.prototype.unhookAll_ = function() { + if (this.origOnError_) { + window.onerror = this.origOnError_; + this.origOnError_ = null; + + _assert = goog.asserts.assert(this.origAssert_); + this.origAssert_ = null; + + fail = goog.asserts.assert(this.origFail_); + this.origFail_ = null; + } +}; + + +/** + * Enables the timeout timer. This timer fires unless continueTesting is + * called. + * @private + */ +goog.testing.AsyncTestCase.prototype.startTimeoutTimer_ = function() { + if (!this.timeoutHandle_ && this.stepTimeout > 0) { + this.timeoutHandle_ = this.timeout(goog.bind(function() { + this.dbgLog_('Timeout timer fired with id ' + this.timeoutHandle_); + this.timeoutHandle_ = 0; + + this.doTopOfStackAsyncError_( + 'Timed out while waiting for ' + + 'continueTesting() to be called.'); + }, this), this.stepTimeout); + this.dbgLog_('Started timeout timer with id ' + this.timeoutHandle_); + } +}; + + +/** + * Disables the timeout timer. + * @private + */ +goog.testing.AsyncTestCase.prototype.stopTimeoutTimer_ = function() { + if (this.timeoutHandle_) { + this.dbgLog_('Clearing timeout timer with id ' + this.timeoutHandle_); + this.clearTimeout(this.timeoutHandle_); + this.timeoutHandle_ = 0; + } +}; + + +/** + * Sets the next function to call in our sequence of async callbacks. + * @param {?function(this:goog.testing.AsyncTestCase, ...?)} func + * The function that executes the next step. + * @param {string} name A description of the next step. + * @private + */ +goog.testing.AsyncTestCase.prototype.setNextStep_ = function(func, name) { + this.nextStepFunc_ = func && goog.bind(func, this); + this.nextStepName_ = name; +}; + + +/** + * Calls the given function, redirecting any exceptions to doAsyncError. + * @param {Function} func The function to call. + * @return {!goog.testing.AsyncTestCase.TopStackFuncResult_} Returns a + * TopStackFuncResult_. + * @private + */ +goog.testing.AsyncTestCase.prototype.callTopOfStackFunc_ = function(func) { + + try { + func.call(this); + return {controlBreakingExceptionThrown: false, message: ''}; + } catch (e) { + this.dbgLog_('Caught exception in callTopOfStackFunc_'); + + try { + this.doAsyncError(e); + return {controlBreakingExceptionThrown: false, message: ''}; + } catch (e2) { + if (!e2.isControlBreakingException) { + throw e2; + } + return {controlBreakingExceptionThrown: true, message: e2.message}; + } + } +}; + + +/** + * Calls the next callback when the isReady_ flag is true. + * @param {Function=} opt_doFirst A function to call before pumping. + * @private + * @throws Throws a ControlBreakingException if there were any failing steps. + */ +goog.testing.AsyncTestCase.prototype.pump_ = function(opt_doFirst) { + // If this function is already above us in the call-stack, then we should + // return rather than pumping in order to minimize call-stack depth. + if (!this.returnWillPump_) { + this.setBatchTime(this.now()); + this.returnWillPump_ = true; + var topFuncResult = {}; + + if (opt_doFirst) { + topFuncResult = this.callTopOfStackFunc_(opt_doFirst); + } + // Note: we don't check for this.running here because it is not set to true + // while executing setUpPage and tearDownPage. + // Also, if isReady_ is false, then one of two things will happen: + // 1. Our timeout callback will be called. + // 2. The tests will call continueTesting(), which will call pump_() again. + while (this.isReady_ && this.nextStepFunc_ && + !topFuncResult.controlBreakingExceptionThrown) { + this.curStepFunc_ = this.nextStepFunc_; + this.curStepName_ = this.nextStepName_; + this.nextStepFunc_ = null; + this.nextStepName_ = ''; + + this.dbgLog_('Performing step: ' + this.curStepName_); + topFuncResult = + this.callTopOfStackFunc_(/** @type {Function} */ (this.curStepFunc_)); + + // If the max run time is exceeded call this function again async so as + // not to block the browser. + var delta = this.now() - this.getBatchTime(); + if (delta > goog.testing.TestCase.maxRunTime && + !topFuncResult.controlBreakingExceptionThrown) { + this.saveMessage('Breaking async'); + var self = this; + this.timeout(function() { self.pump_(); }, 100); + break; + } + } + this.returnWillPump_ = false; + } else if (opt_doFirst) { + opt_doFirst.call(this); + } +}; + + +/** + * Sets up the test page and then waits until the test case has been marked + * as ready before executing the tests. + * @private + */ +goog.testing.AsyncTestCase.prototype.doSetUpPage_ = function() { + this.setNextStep_(this.execute, 'TestCase.execute'); + this.setUpPage(); +}; + + +/** + * Step 1: Move to the next test. + * @private + */ +goog.testing.AsyncTestCase.prototype.doIteration_ = function() { + this.expectedSignalCount_ = 0; + this.receivedSignalCount_ = 0; + this.activeTest = this.next(); + goog.testing.TestCase.currentTestName = + this.activeTest ? this.activeTest.name : null; + if (this.activeTest && this.running) { + this.result_.runCount++; + // If this test should be marked as having failed, doIteration will go + // straight to the next test. + if (this.maybeFailTestEarly(this.activeTest)) { + this.setNextStep_(this.doIteration_, 'doIteration'); + } else { + this.setNextStep_(this.doSetUp_, 'setUp'); + } + } else { + // All tests done. + this.finalize(); + } +}; + + +/** + * Step 2: Call setUp(). + * @private + */ +goog.testing.AsyncTestCase.prototype.doSetUp_ = function() { + this.log('Running test: ' + this.activeTest.name); + this.cleanedUp_ = false; + this.setNextStep_(this.doExecute_, this.activeTest.name); + this.setUp(); +}; + + +/** + * Step 3: Call test.execute(). + * @private + */ +goog.testing.AsyncTestCase.prototype.doExecute_ = function() { + this.setNextStep_(this.doTearDown_, 'tearDown'); + this.activeTest.execute(); +}; + + +/** + * Step 4: Call tearDown(). + * @private + */ +goog.testing.AsyncTestCase.prototype.doTearDown_ = function() { + this.cleanedUp_ = true; + this.setNextStep_(this.doNext_, 'doNext'); + this.tearDown(); +}; + + +/** + * Step 5: Call doSuccess() + * @private + */ +goog.testing.AsyncTestCase.prototype.doNext_ = function() { + this.setNextStep_(this.doIteration_, 'doIteration'); + this.doSuccess(/** @type {goog.testing.TestCase.Test} */ (this.activeTest)); +}; diff --git a/closure-library/closure/goog/testing/benchmark.js b/closure-library/closure/goog/testing/benchmark.js new file mode 100644 index 0000000000..481b8eac71 --- /dev/null +++ b/closure-library/closure/goog/testing/benchmark.js @@ -0,0 +1,96 @@ +// Copyright 2010 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.testing.benchmark'); +goog.setTestOnly('goog.testing.benchmark'); + +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.testing.PerformanceTable'); +goog.require('goog.testing.PerformanceTimer'); +goog.require('goog.testing.TestCase'); + + +/** + * Run the benchmarks. + * @private + */ +goog.testing.benchmark.run_ = function() { + // Parse the 'times' query parameter if it's set. + var times = 200; + var search = window.location.search; + var timesMatch = search.match(/(?:\?|&)times=([^?&]+)/i); + if (timesMatch) { + times = Number(timesMatch[1]); + } + + var prefix = 'benchmark'; + + // First, get the functions. + var testSources = goog.testing.TestCase.getGlobals(); + + var benchmarks = {}; + var names = []; + + for (var i = 0; i < testSources.length; i++) { + var testSource = testSources[i]; + for (var name in testSource) { + if ((new RegExp('^' + prefix)).test(name)) { + var ref; + try { + ref = testSource[name]; + } catch (ex) { + // NOTE(brenneman): When running tests from a file:// URL on Firefox + // 3.5 for Windows, any reference to window.sessionStorage raises + // an "Operation is not supported" exception. Ignore any exceptions + // raised by simply accessing global properties. + ref = undefined; + } + + if (goog.isFunction(ref)) { + benchmarks[name] = ref; + names.push(name); + } + } + } + } + + document.body.appendChild( + goog.dom.createTextNode( + 'Running ' + names.length + ' benchmarks ' + times + ' times each.')); + document.body.appendChild(goog.dom.createElement(goog.dom.TagName.BR)); + + names.sort(); + + // Build a table and timer. + var performanceTimer = new goog.testing.PerformanceTimer(times); + performanceTimer.setDiscardOutliers(true); + + var performanceTable = + new goog.testing.PerformanceTable(document.body, performanceTimer, 2); + + // Next, run the benchmarks. + for (var i = 0; i < names.length; i++) { + performanceTable.run(benchmarks[names[i]], names[i]); + } +}; + + +/** + * Onload handler that runs the benchmarks. + * @param {Event} e The event object. + */ +window.onload = function(e) { + goog.testing.benchmark.run_(); +}; diff --git a/closure-library/closure/goog/testing/continuationtestcase.js b/closure-library/closure/goog/testing/continuationtestcase.js new file mode 100644 index 0000000000..7a464f665b --- /dev/null +++ b/closure-library/closure/goog/testing/continuationtestcase.js @@ -0,0 +1,686 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Defines test classes for tests that can wait for conditions. + * + * Normal unit tests must complete their test logic within a single function + * execution. This is ideal for most tests, but makes it difficult to test + * routines that require real time to complete. The tests and TestCase in this + * file allow for tests that can wait until a condition is true before + * continuing execution. + * + * Each test has the typical three phases of execution: setUp, the test itself, + * and tearDown. During each phase, the test function may add wait conditions, + * which result in new test steps being added for that phase. All steps in a + * given phase must complete before moving on to the next phase. An error in + * any phase will stop that test and report the error to the test runner. + * + * This class should not be used where adequate mocks exist. Time-based routines + * should use the MockClock, which runs much faster and provides equivalent + * results. Continuation tests should be used for testing code that depends on + * browser behaviors that are difficult to mock. For example, testing code that + * relies on Iframe load events, event or layout code that requires a setTimeout + * to become valid, and other browser-dependent native object interactions for + * which mocks are insufficient. + * + * Sample usage: + * + *
+ * var testCase = new goog.testing.ContinuationTestCase();
+ * testCase.autoDiscoverTests();
+ *
+ * if (typeof G_testRunner != 'undefined') {
+ *   G_testRunner.initialize(testCase);
+ * }
+ *
+ * function testWaiting() {
+ *   var someVar = true;
+ *   waitForTimeout(function() {
+ *     assertTrue(someVar)
+ *   }, 500);
+ * }
+ *
+ * function testWaitForEvent() {
+ *   var et = goog.events.EventTarget();
+ *   waitForEvent(et, 'test', function() {
+ *     // Test step runs after the event fires.
+ *   })
+ *   et.dispatchEvent(et, 'test');
+ * }
+ *
+ * function testWaitForCondition() {
+ *   var counter = 0;
+ *
+ *   waitForCondition(function() {
+ *     // This function is evaluated periodically until it returns true, or it
+ *     // times out.
+ *     return ++counter >= 3;
+ *   }, function() {
+ *     // This test step is run once the condition becomes true.
+ *     assertEquals(3, counter);
+ *   });
+ * }
+ * 
+ * + * @author brenneman@google.com (Shawn Brenneman) + */ + + +goog.setTestOnly('goog.testing.ContinuationTestCase'); +goog.provide('goog.testing.ContinuationTestCase'); +goog.provide('goog.testing.ContinuationTestCase.ContinuationTest'); +goog.provide('goog.testing.ContinuationTestCase.Step'); + +goog.require('goog.array'); +goog.require('goog.events.EventHandler'); +goog.require('goog.events.EventTarget'); +goog.require('goog.testing.TestCase'); +goog.require('goog.testing.asserts'); + + + +/** + * Constructs a test case that supports tests with continuations. Test functions + * may issue "wait" commands that suspend the test temporarily and continue once + * the wait condition is met. + * + * @param {string=} opt_name Optional name for the test case. + * @constructor + * @extends {goog.testing.TestCase} + * @deprecated ContinuationTestCase is deprecated. Prefer returning Promises + * for tests that assert Asynchronous behavior. + * @final + */ +goog.testing.ContinuationTestCase = function(opt_name) { + goog.testing.TestCase.call(this, opt_name); + + /** + * An event handler for waiting on Closure or browser events during tests. + * @type {goog.events.EventHandler} + * @private + */ + this.handler_ = new goog.events.EventHandler(this); +}; +goog.inherits(goog.testing.ContinuationTestCase, goog.testing.TestCase); + + +/** + * The default maximum time to wait for a single test step in milliseconds. + * @type {number} + */ +goog.testing.ContinuationTestCase.MAX_TIMEOUT = 1000; + + +/** + * Lock used to prevent multiple test steps from running recursively. + * @type {boolean} + * @private + */ +goog.testing.ContinuationTestCase.prototype.locked_; + + +/** + * The current test being run. + * @type {goog.testing.ContinuationTestCase.ContinuationTest} + * @private + */ +goog.testing.ContinuationTestCase.prototype.currentTest_ = null; + + +/** + * Enables or disables the wait functions in the global scope. + * @param {boolean} enable Whether the wait functions should be exported. + * @private + */ +goog.testing.ContinuationTestCase.prototype.enableWaitFunctions_ = function( + enable) { + if (enable) { + goog.exportSymbol( + 'waitForCondition', goog.bind(this.waitForCondition, this)); + goog.exportSymbol('waitForEvent', goog.bind(this.waitForEvent, this)); + goog.exportSymbol('waitForTimeout', goog.bind(this.waitForTimeout, this)); + } else { + // Internet Explorer doesn't allow deletion of properties on the window. + goog.global['waitForCondition'] = undefined; + goog.global['waitForEvent'] = undefined; + goog.global['waitForTimeout'] = undefined; + } +}; + + +/** @override */ +goog.testing.ContinuationTestCase.prototype.runTests = function() { + this.enableWaitFunctions_(true); + goog.testing.ContinuationTestCase.superClass_.runTests.call(this); +}; + + +/** @override */ +goog.testing.ContinuationTestCase.prototype.finalize = function() { + this.enableWaitFunctions_(false); + goog.testing.ContinuationTestCase.superClass_.finalize.call(this); +}; + + +/** @override */ +goog.testing.ContinuationTestCase.prototype.cycleTests = function() { + // Get the next test in the queue. + if (!this.currentTest_) { + this.currentTest_ = this.createNextTest_(); + } + + // Run the next step of the current test, or exit if all tests are complete. + if (this.currentTest_) { + this.runNextStep_(); + } else { + this.finalize(); + } +}; + + +/** + * Creates the next test in the queue. + * @return {goog.testing.ContinuationTestCase.ContinuationTest} The next test to + * execute, or null if no pending tests remain. + * @private + */ +goog.testing.ContinuationTestCase.prototype.createNextTest_ = function() { + var test = this.next(); + if (!test) { + return null; + } + + + var name = test.name; + goog.testing.TestCase.currentTestName = name; + this.result_.runCount++; + this.log('Running test: ' + name); + + return new goog.testing.ContinuationTestCase.ContinuationTest( + new goog.testing.TestCase.Test(name, this.setUp, this), test, + new goog.testing.TestCase.Test(name, this.tearDown, this)); +}; + + +/** + * Cleans up a finished test and cycles to the next test. + * @private + */ +goog.testing.ContinuationTestCase.prototype.finishTest_ = function() { + var err = this.currentTest_.getError(); + if (err) { + this.recordError(this.currentTest_.name, err); + this.doError(this.currentTest_); + } else { + this.doSuccess(this.currentTest_); + } + + goog.testing.TestCase.currentTestName = null; + this.currentTest_ = null; + this.locked_ = false; + this.handler_.removeAll(); + + this.timeout(goog.bind(this.cycleTests, this), 0); +}; + + +/** + * Executes the next step in the current phase, advancing through each phase as + * all steps are completed. + * @private + */ +goog.testing.ContinuationTestCase.prototype.runNextStep_ = function() { + if (this.locked_) { + // Attempting to run a step before the previous step has finished. Try again + // after that step has released the lock. + return; + } + + var phase = this.currentTest_.getCurrentPhase(); + + if (!phase || !phase.length) { + // No more steps for this test. + this.finishTest_(); + return; + } + + // Find the next step that is not in a wait state. + var stepIndex = + goog.array.findIndex(phase, function(step) { return !step.waiting; }); + + if (stepIndex < 0) { + // All active steps are currently waiting. Return until one wakes up. + return; + } + + this.locked_ = true; + var step = phase[stepIndex]; + + try { + step.execute(); + // Remove the successfully completed step. If an error is thrown, all steps + // will be removed for this phase. + goog.array.removeAt(phase, stepIndex); + + } catch (e) { + this.currentTest_.setError(e); + + // An assertion has failed, or an exception was raised. Clear the current + // phase, whether it is setUp, test, or tearDown. + this.currentTest_.cancelCurrentPhase(); + + // Cancel the setUp and test phase no matter where the error occurred. The + // tearDown phase will still run if it has pending steps. + this.currentTest_.cancelTestPhase(); + } + + this.locked_ = false; + this.runNextStep_(); +}; + + +/** + * Creates a new test step that will run after a user-specified + * timeout. No guarantee is made on the execution order of the + * continuation, except for those provided by each browser's + * window.setTimeout. In particular, if two continuations are + * registered at the same time with very small delta for their + * durations, this class can not guarantee that the continuation with + * the smaller duration will be executed first. + * @param {function()} continuation The test function to invoke after the timeout. + * @param {number=} opt_duration The length of the timeout in milliseconds. + */ +goog.testing.ContinuationTestCase.prototype.waitForTimeout = function( + continuation, opt_duration) { + var step = this.addStep_(continuation); + step.setTimeout( + goog.bind(this.handleComplete_, this, step), opt_duration || 0); +}; + + +/** + * Creates a new test step that will run after an event has fired. If the event + * does not fire within a reasonable timeout, the test will fail. + * @param {goog.events.EventTarget|EventTarget} eventTarget The target that will + * fire the event. + * @param {string} eventType The type of event to listen for. + * @param {function()} continuation The test function to invoke after the event + * fires. + */ +goog.testing.ContinuationTestCase.prototype.waitForEvent = function( + eventTarget, eventType, continuation) { + + var step = this.addStep_(continuation); + + var duration = goog.testing.ContinuationTestCase.MAX_TIMEOUT; + step.setTimeout( + goog.bind(this.handleTimeout_, this, step, duration), duration); + + this.handler_.listenOnce( + eventTarget, eventType, goog.bind(this.handleComplete_, this, step)); +}; + + +/** + * Creates a new test step which will run once a condition becomes true. The + * condition will be polled at a user-specified interval until it becomes true, + * or until a maximum timeout is reached. + * @param {Function} condition The condition to poll. + * @param {function()} continuation The test code to evaluate once the condition + * becomes true. + * @param {number=} opt_interval The polling interval in milliseconds. + * @param {number=} opt_maxTimeout The maximum amount of time to wait for the + * condition in milliseconds (defaults to 1000). + */ +goog.testing.ContinuationTestCase.prototype.waitForCondition = function( + condition, continuation, opt_interval, opt_maxTimeout) { + + var interval = opt_interval || 100; + var timeout = opt_maxTimeout || goog.testing.ContinuationTestCase.MAX_TIMEOUT; + + var step = this.addStep_(continuation); + this.testCondition_(step, condition, goog.now(), interval, timeout); +}; + + +/** + * Creates a new asynchronous test step which will be added to the current test + * phase. + * @param {function()} func The test function that will be executed for this step. + * @return {!goog.testing.ContinuationTestCase.Step} A new test step. + * @private + */ +goog.testing.ContinuationTestCase.prototype.addStep_ = function(func) { + if (!this.currentTest_) { + throw new Error('Cannot add test steps outside of a running test.'); + } + + var step = new goog.testing.ContinuationTestCase.Step( + this.currentTest_.name, func, this.currentTest_.scope); + this.currentTest_.addStep(step); + return step; +}; + + +/** + * Handles completion of a step's wait condition. Advances the test, allowing + * the step's test method to run. + * @param {goog.testing.ContinuationTestCase.Step} step The step that has + * finished waiting. + * @private + */ +goog.testing.ContinuationTestCase.prototype.handleComplete_ = function(step) { + step.clearTimeout(); + step.waiting = false; + this.runNextStep_(); +}; + + +/** + * Handles the timeout event for a step that has exceeded the maximum time. This + * causes the current test to fail. + * @param {goog.testing.ContinuationTestCase.Step} step The timed-out step. + * @param {number} duration The length of the timeout in milliseconds. + * @private + */ +goog.testing.ContinuationTestCase.prototype.handleTimeout_ = function( + step, duration) { + step.ref = function() { + fail('Continuation timed out after ' + duration + 'ms.'); + }; + + // Since the test is failing, cancel any other pending event listeners. + this.handler_.removeAll(); + this.handleComplete_(step); +}; + + +/** + * Tests a wait condition and executes the associated test step once the + * condition is true. + * + * If the condition does not become true before the maximum duration, the + * interval will stop and the test step will fail in the kill timer. + * + * @param {goog.testing.ContinuationTestCase.Step} step The waiting test step. + * @param {Function} condition The test condition. + * @param {number} startTime Time when the test step began waiting. + * @param {number} interval The duration in milliseconds to wait between tests. + * @param {number} timeout The maximum amount of time to wait for the condition + * to become true. Measured from the startTime in milliseconds. + * @private + */ +goog.testing.ContinuationTestCase.prototype.testCondition_ = function( + step, condition, startTime, interval, timeout) { + + var duration = goog.now() - startTime; + + if (condition()) { + this.handleComplete_(step); + } else if (duration < timeout) { + step.setTimeout( + goog.bind( + this.testCondition_, this, step, condition, startTime, interval, + timeout), + interval); + } else { + this.handleTimeout_(step, duration); + } +}; + + + +/** + * Creates a continuation test case, which consists of multiple test steps that + * occur in several phases. + * + * The steps are distributed between setUp, test, and tearDown phases. During + * the execution of each step, 0 or more steps may be added to the current + * phase. Once all steps in a phase have completed, the next phase will be + * executed. + * + * If any errors occur (such as an assertion failure), the setUp and Test phases + * will be cancelled immediately. The tearDown phase will always start, but may + * be cancelled as well if it raises an error. + * + * @param {goog.testing.TestCase.Test} setUp A setUp test method to run before + * the main test phase. + * @param {goog.testing.TestCase.Test} test A test method to run. + * @param {goog.testing.TestCase.Test} tearDown A tearDown test method to run + * after the test method completes or fails. + * @constructor + * @extends {goog.testing.TestCase.Test} + * @final + */ +goog.testing.ContinuationTestCase.ContinuationTest = function( + setUp, test, tearDown) { + // This test container has a name, but no evaluation function or scope. + goog.testing.TestCase.Test.call(this, test.name, function() {}, null); + + /** + * The list of test steps to run during setUp. + * @type {Array} + * @private + */ + this.setUp_ = [setUp]; + + /** + * The list of test steps to run for the actual test. + * @type {Array} + * @private + */ + this.test_ = [test]; + + /** + * The list of test steps to run during the tearDown phase. + * @type {Array} + * @private + */ + this.tearDown_ = [tearDown]; +}; +goog.inherits( + goog.testing.ContinuationTestCase.ContinuationTest, + goog.testing.TestCase.Test); + + +/** + * The first error encountered during the test run, if any. + * @type {Error} + * @private + */ +goog.testing.ContinuationTestCase.ContinuationTest.prototype.error_ = null; + + +/** + * @return {Error} The first error to be raised during the test run or null if + * no errors occurred. + */ +goog.testing.ContinuationTestCase.ContinuationTest.prototype.getError = + function() { + return this.error_; +}; + + +/** + * Sets an error for the test so it can be reported. Only the first error set + * during a test will be reported. Additional errors that occur in later test + * phases will be discarded. + * @param {Error} e An error. + */ +goog.testing.ContinuationTestCase.ContinuationTest.prototype.setError = + function(e) { + this.error_ = this.error_ || e; +}; + + +/** + * @return {Array} The current phase of steps + * being processed. Returns null if all steps have been completed. + */ +goog.testing.ContinuationTestCase.ContinuationTest.prototype.getCurrentPhase = + function() { + if (this.setUp_.length) { + return this.setUp_; + } + + if (this.test_.length) { + return this.test_; + } + + if (this.tearDown_.length) { + return this.tearDown_; + } + + return null; +}; + + +/** + * Adds a new test step to the end of the current phase. The new step will wait + * for a condition to be met before running, or will fail after a timeout. + * @param {!goog.testing.ContinuationTestCase.Step} step The test step to add. + */ +goog.testing.ContinuationTestCase.ContinuationTest.prototype.addStep = function( + step) { + var phase = this.getCurrentPhase(); + if (phase) { + phase.push(step); + } else { + throw new Error('Attempted to add a step to a completed test.'); + } +}; + + +/** + * Cancels all remaining steps in the current phase. Called after an error in + * any phase occurs. + */ +goog.testing.ContinuationTestCase.ContinuationTest.prototype + .cancelCurrentPhase = function() { + this.cancelPhase_(this.getCurrentPhase()); +}; + + +/** + * Skips the rest of the setUp and test phases, but leaves the tearDown phase to + * clean up. + */ +goog.testing.ContinuationTestCase.ContinuationTest.prototype.cancelTestPhase = + function() { + this.cancelPhase_(this.setUp_); + this.cancelPhase_(this.test_); +}; + + +/** + * Clears a test phase and cancels any pending steps found. + * @param {Array} phase A list of test steps. + * @private + */ +goog.testing.ContinuationTestCase.ContinuationTest.prototype.cancelPhase_ = + function(phase) { + while (phase && phase.length) { + var step = phase.pop(); + if (step instanceof goog.testing.ContinuationTestCase.Step) { + step.clearTimeout(); + } + } +}; + + + +/** + * Constructs a single step in a larger continuation test. Each step is similar + * to a typical TestCase test, except it may wait for an event or timeout to + * occur before running the test function. + * + * @param {string} name The test name. + * @param {function()} ref The test function to run. + * @param {Object=} opt_scope The object context to run the test in. + * @constructor + * @extends {goog.testing.TestCase.Test} + * @final + */ +goog.testing.ContinuationTestCase.Step = function(name, ref, opt_scope) { + goog.testing.TestCase.Test.call(this, name, ref, opt_scope); +}; +goog.inherits( + goog.testing.ContinuationTestCase.Step, goog.testing.TestCase.Test); + + +/** + * Whether the step is currently waiting for a condition to continue. All new + * steps begin in wait state. + * @override + */ +goog.testing.ContinuationTestCase.Step.prototype.waiting = true; + + +/** + * A saved reference to window.clearTimeout so that MockClock or other overrides + * don't affect continuation timeouts. + * @type {Function} + * @private + */ +goog.testing.ContinuationTestCase.Step.protectedClearTimeout_ = + window.clearTimeout; + + +/** + * A saved reference to window.setTimeout so that MockClock or other overrides + * don't affect continuation timeouts. + * @type {Function} + * @private + */ +goog.testing.ContinuationTestCase.Step.protectedSetTimeout_ = window.setTimeout; + + +/** + * Key to this step's timeout. If the step is waiting for an event, the timeout + * will be used as a kill timer. If the step is waiting + * @type {number} + * @private + */ +goog.testing.ContinuationTestCase.Step.prototype.timeout_; + + +/** + * Starts a timeout for this step. Each step may have only one timeout active at + * a time. + * @param {Function} func The function to call after the timeout. + * @param {number} duration The number of milliseconds to wait before invoking + * the function. + */ +goog.testing.ContinuationTestCase.Step.prototype.setTimeout = function( + func, duration) { + + this.clearTimeout(); + + var setTimeout = goog.testing.ContinuationTestCase.Step.protectedSetTimeout_; + this.timeout_ = setTimeout(func, duration); +}; + + +/** + * Clears the current timeout if it is active. + */ +goog.testing.ContinuationTestCase.Step.prototype.clearTimeout = function() { + if (this.timeout_) { + var clear = goog.testing.ContinuationTestCase.Step.protectedClearTimeout_; + + clear(this.timeout_); + delete this.timeout_; + } +}; diff --git a/closure-library/closure/goog/testing/deferredtestcase.js b/closure-library/closure/goog/testing/deferredtestcase.js new file mode 100644 index 0000000000..ebee85c175 --- /dev/null +++ b/closure-library/closure/goog/testing/deferredtestcase.js @@ -0,0 +1,158 @@ +// Copyright 2010 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Defines DeferredTestCase class. By calling waitForDeferred(), + * tests in DeferredTestCase can wait for a Deferred object to complete its + * callbacks before continuing to the next test. + * + * Example Usage: + * + * var deferredTestCase = goog.testing.DeferredTestCase.createAndInstall(); + * // Optionally, set a longer-than-usual step timeout. + * deferredTestCase.stepTimeout = 15 * 1000; // 15 seconds + * + * function testDeferredCallbacks() { + * var callbackTime = goog.now(); + * var callbacks = new goog.async.Deferred(); + * deferredTestCase.addWaitForAsync('Waiting for 1st callback', callbacks); + * callbacks.addCallback( + * function() { + * assertTrue( + * 'We\'re going back in time!', goog.now() >= callbackTime); + * callbackTime = goog.now(); + * }); + * deferredTestCase.addWaitForAsync('Waiting for 2nd callback', callbacks); + * callbacks.addCallback( + * function() { + * assertTrue( + * 'We\'re going back in time!', goog.now() >= callbackTime); + * callbackTime = goog.now(); + * }); + * deferredTestCase.addWaitForAsync('Waiting for last callback', callbacks); + * callbacks.addCallback( + * function() { + * assertTrue( + * 'We\'re going back in time!', goog.now() >= callbackTime); + * callbackTime = goog.now(); + * }); + * + * deferredTestCase.waitForDeferred(callbacks); + * } + * + * Note that DeferredTestCase still preserves the functionality of + * AsyncTestCase. + * + * @see.goog.async.Deferred + * @see goog.testing.AsyncTestCase + */ + +goog.setTestOnly('goog.testing.DeferredTestCase'); +goog.provide('goog.testing.DeferredTestCase'); + +goog.require('goog.async.Deferred'); +goog.require('goog.testing.AsyncTestCase'); +goog.require('goog.testing.TestCase'); + + + +/** + * A test case that can asynchronously wait on a Deferred object. + * @param {string=} opt_name A descriptive name for the test case. + * @constructor + * @extends {goog.testing.AsyncTestCase} + * @deprecated Use goog.testing.TestCase instead. goog.testing.TestCase now + * supports async testing using promises. + */ +goog.testing.DeferredTestCase = function(opt_name) { + goog.testing.AsyncTestCase.call(this, opt_name); +}; +goog.inherits(goog.testing.DeferredTestCase, goog.testing.AsyncTestCase); + + +/** + * Preferred way of creating a DeferredTestCase. Creates one and initializes it + * with the G_testRunner. + * @param {string=} opt_name A descriptive name for the test case. + * @return {!goog.testing.DeferredTestCase} The created DeferredTestCase. + */ +goog.testing.DeferredTestCase.createAndInstall = function(opt_name) { + var deferredTestCase = new goog.testing.DeferredTestCase(opt_name); + goog.testing.TestCase.initializeTestRunner(deferredTestCase); + return deferredTestCase; +}; + + +/** + * Handler for when the test produces an error. + * @param {Error|string} err The error object. + * @protected + * @throws Always throws a ControlBreakingException. + */ +goog.testing.DeferredTestCase.prototype.onError = function(err) { + this.doAsyncError(err); +}; + + +/** + * Handler for when the test succeeds. + * @protected + */ +goog.testing.DeferredTestCase.prototype.onSuccess = function() { + this.continueTesting(); +}; + + +/** + * Adds a callback to update the wait message of this async test case. Using + * this method generously also helps to document the test flow. + * @param {string} msg The update wait status message. + * @param {goog.async.Deferred} d The deferred object to add the waitForAsync + * callback to. + * @see goog.testing.AsyncTestCase#waitForAsync + */ +goog.testing.DeferredTestCase.prototype.addWaitForAsync = function(msg, d) { + d.addCallback(goog.bind(this.waitForAsync, this, msg)); +}; + + +/** + * Wires up given Deferred object to the test case, then starts the + * goog.async.Deferred object's callback. + * @param {string|!goog.async.Deferred} a The wait status message or the + * deferred object to wait for. + * @param {goog.async.Deferred=} opt_b The deferred object to wait for. + */ +goog.testing.DeferredTestCase.prototype.waitForDeferred = function(a, opt_b) { + var waitMsg; + var deferred; + switch (arguments.length) { + case 1: + deferred = a; + waitMsg = null; + break; + case 2: + deferred = opt_b; + waitMsg = a; + break; + default: // Shouldn't be here in compiled mode + throw new Error('Invalid number of arguments'); + } + deferred.addCallbacks(this.onSuccess, this.onError, this); + if (!waitMsg) { + waitMsg = 'Waiting for deferred in ' + this.getCurrentStepName(); + } + this.waitForAsync(/** @type {string} */ (waitMsg)); + deferred.callback(true); +}; diff --git a/closure-library/closure/goog/testing/dom.js b/closure-library/closure/goog/testing/dom.js new file mode 100644 index 0000000000..5da868ec53 --- /dev/null +++ b/closure-library/closure/goog/testing/dom.js @@ -0,0 +1,693 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Testing utilities for DOM related tests. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.setTestOnly('goog.testing.dom'); +goog.provide('goog.testing.dom'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.AbstractRange'); +goog.require('goog.dom.InputType'); +goog.require('goog.dom.NodeIterator'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.TagIterator'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.classlist'); +goog.require('goog.iter'); +goog.require('goog.object'); +goog.require('goog.string'); +goog.require('goog.style'); +goog.require('goog.testing.asserts'); +goog.require('goog.userAgent'); + + +/** + * @return {!Node} A DIV node with a unique ID identifying the + * `END_TAG_MARKER_`. + * @private + */ +goog.testing.dom.createEndTagMarker_ = function() { + var marker = goog.dom.createElement(goog.dom.TagName.DIV); + marker.id = goog.getUid(marker); + return marker; +}; + + +/** + * A unique object to use as an end tag marker. + * @private {!Node} + * @const + */ +goog.testing.dom.END_TAG_MARKER_ = goog.testing.dom.createEndTagMarker_(); + + +/** + * Tests if the given iterator over nodes matches the given Array of node + * descriptors. Throws an error if any match fails. + * @param {goog.iter.Iterator} it An iterator over nodes. + * @param {Array} array Array of node descriptors to match + * against. Node descriptors can be any of the following: + * Node: Test if the two nodes are equal. + * number: Test node.nodeType == number. + * string starting with '#': Match the node's id with the text + * after "#". + * other string: Match the text node's contents. + */ +goog.testing.dom.assertNodesMatch = function(it, array) { + var i = 0; + goog.iter.forEach(it, function(node) { + if (array.length <= i) { + fail( + 'Got more nodes than expected: ' + + goog.testing.dom.describeNode_(node)); + } + var expected = array[i]; + + if (goog.dom.isNodeLike(expected)) { + assertEquals('Nodes should match at position ' + i, expected, node); + } else if (goog.isNumber(expected)) { + assertEquals( + 'Node types should match at position ' + i, expected, node.nodeType); + } else if (expected.charAt(0) == '#') { + assertEquals( + 'Expected element at position ' + i, goog.dom.NodeType.ELEMENT, + node.nodeType); + var expectedId = expected.substr(1); + assertEquals('IDs should match at position ' + i, expectedId, node.id); + + } else { + assertEquals( + 'Expected text node at position ' + i, goog.dom.NodeType.TEXT, + node.nodeType); + assertEquals( + 'Node contents should match at position ' + i, expected, + node.nodeValue); + } + + i++; + }); + + assertEquals('Used entire match array', array.length, i); +}; + + +/** + * Exposes a node as a string. + * @param {Node} node A node. + * @return {string} A string representation of the node. + */ +goog.testing.dom.exposeNode = function(node) { + node = /** @type {!Element} */ (node); + var result = node.nodeName || node.nodeValue; + if (node.id) { + result += '#' + node.id; + } + result += ':"' + (node.innerHTML || '') + '"'; + return result; +}; + + +/** + * Exposes the nodes of a range wrapper as a string. + * @param {goog.dom.AbstractRange} range A range. + * @return {string} A string representation of the range. + */ +goog.testing.dom.exposeRange = function(range) { + // This is deliberately not implemented as + // goog.dom.AbstractRange.prototype.toString, because it is non-authoritative. + // Two equivalent ranges may have very different exposeRange values, and + // two different ranges may have equal exposeRange values. + // (The mapping of ranges to DOM nodes/offsets is a many-to-many mapping). + if (!range) { + return 'null'; + } + return goog.testing.dom.exposeNode(range.getStartNode()) + ':' + + range.getStartOffset() + ' to ' + + goog.testing.dom.exposeNode(range.getEndNode()) + ':' + + range.getEndOffset(); +}; + + +/** + * Determines if the current user agent matches the specified string. Returns + * false if the string does specify at least one user agent but does not match + * the running agent. + * @param {string} userAgents Space delimited string of user agents. + * @return {boolean} Whether the user agent was matched. Also true if no user + * agent was listed in the expectation string. + * @private + */ +goog.testing.dom.checkUserAgents_ = function(userAgents) { + if (goog.string.startsWith(userAgents, '!')) { + if (goog.string.contains(userAgents, ' ')) { + throw new Error('Only a single negative user agent may be specified'); + } + return !goog.userAgent[userAgents.substr(1)]; + } + + var agents = userAgents.split(' '); + var hasUserAgent = false; + for (var i = 0, len = agents.length; i < len; i++) { + var cls = agents[i]; + if (cls in goog.userAgent) { + hasUserAgent = true; + if (goog.userAgent[cls]) { + return true; + } + } + } + // If we got here, there was a user agent listed but we didn't match it. + return !hasUserAgent; +}; + + +/** + * Map function that converts end tags to a specific object. + * @param {Node} node The node to map. + * @param {undefined} ignore Always undefined. + * @param {!goog.iter.Iterator} iterator The iterator. + * @return {Node} The resulting iteration item. + * @private + */ +goog.testing.dom.endTagMap_ = function(node, ignore, iterator) { + goog.asserts.assertInstanceof(iterator, goog.dom.TagIterator); + return iterator.isEndTag() ? goog.testing.dom.END_TAG_MARKER_ : node; +}; + + +/** + * Check if the given node is important. + * + * A node is important if it is + * - a non-empty text node; or, + * - or an element annotated to match on this user agent; or, + * - a non-annotated element + * + * @param {Node} node The node to test. + * @return {boolean} Whether this node should be included for iteration. + * @private + */ +goog.testing.dom.nodeFilter_ = function(node) { + if (node.nodeType == goog.dom.NodeType.TEXT) { + // If a node is part of a string of text nodes and it has spaces in it, + // we allow it since it's going to affect the merging of nodes done below. + if (goog.string.isBreakingWhitespace(node.nodeValue) && + (!node.previousSibling || + node.previousSibling.nodeType != goog.dom.NodeType.TEXT) && + (!node.nextSibling || + node.nextSibling.nodeType != goog.dom.NodeType.TEXT)) { + return false; + } + // Allow optional text to be specified as [[BROWSER1 BROWSER2]]Text + var match = node.nodeValue.match(/^\[\[(.+)\]\]/); + if (match) { + return goog.testing.dom.checkUserAgents_(match[1]); + } + + return true; + } + + // This cast exists to preserve existing behaviour. It's risky, but fine as + // long as we only access direct properties of `node`. + var maybeElement = /** @type {!Element} */ (node); + if (maybeElement.className && goog.isString(maybeElement.className)) { + return goog.testing.dom.checkUserAgents_(maybeElement.className); + } + + return true; +}; + + +/** + * Determines the text to match from the given node, removing browser + * specification strings. + * @param {Node} node The node expected to match. + * @return {string} The text, stripped of browser specification strings. + * @private + */ +goog.testing.dom.getExpectedText_ = function(node) { + // Strip off the browser specifications. + return node.nodeValue.match(/^(\[\[.+\]\])?([\s\S]*)/)[2]; +}; + + +/** + * Describes the given node. + * @param {Node} node The node to describe. + * @return {string} A description of the node. + * @private + */ +goog.testing.dom.describeNode_ = function(node) { + if (node.nodeType == goog.dom.NodeType.TEXT) { + return '[Text: ' + node.nodeValue + ']'; + } else { + // We can't actually be sure this is an Element, but other code depends on + // us pretending it is. + node = /** @type {!Element} */ (node); + return '<' + node.tagName + (node.id ? ' #' + node.id : '') + ' .../>'; + } +}; + + +/** + * Assert that the html in `actual` is substantially similar to + * htmlPattern. This method tests for the same set of styles, for the same + * order of nodes, and the presence of attributes. Breaking whitespace nodes + * are ignored. Elements can be + * annotated with classnames corresponding to keys in goog.userAgent and will be + * expected to show up in that user agent and expected not to show up in + * others. + * @param {string} htmlPattern The pattern to match. + * @param {!Element} actual The element to check: its contents are matched + * against the HTML pattern. + * @param {boolean=} opt_strictAttributes If false, attributes that appear in + * htmlPattern must be in actual, but actual can have attributes not + * present in htmlPattern. If true, htmlPattern and actual must have the + * same set of attributes. Default is false. + */ +goog.testing.dom.assertHtmlContentsMatch = function( + htmlPattern, actual, opt_strictAttributes) { + var div = goog.dom.createDom(goog.dom.TagName.DIV); + div.innerHTML = htmlPattern; + + var errorSuffix = + '\nExpected\n' + div.innerHTML + '\nActual\n' + actual.innerHTML; + + var actualIt = goog.iter.filter( + goog.iter.map( + new goog.dom.TagIterator(actual), goog.testing.dom.endTagMap_), + goog.testing.dom.nodeFilter_); + + var expectedIt = goog.iter.filter( + new goog.dom.NodeIterator(div), goog.testing.dom.nodeFilter_); + + var actualNode; + var preIterated = false; + var advanceActualNode = function() { + // If the iterator has already been advanced, don't advance it again. + if (!preIterated) { + actualNode = goog.iter.nextOrValue(actualIt, null); + } + preIterated = false; + + // Advance the iterator so long as it is return end tags. + while (actualNode == goog.testing.dom.END_TAG_MARKER_) { + actualNode = goog.iter.nextOrValue(actualIt, null); + } + }; + + // HACK(brenneman): IE has unique ideas about whitespace handling when setting + // innerHTML. This results in elision of leading whitespace in the expected + // nodes where doing so doesn't affect visible rendering. As a workaround, we + // remove the leading whitespace in the actual nodes where necessary. + // + // The collapsible variable tracks whether we should collapse the whitespace + // in the next Text node we encounter. + var IE_TEXT_COLLAPSE = + goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9'); + + var collapsible = true; + + var number = 0; + goog.iter.forEach(expectedIt, function(expectedNode) { + advanceActualNode(); + assertNotNull( + 'Finished actual HTML before finishing expected HTML at ' + + 'node number ' + number + ': ' + + goog.testing.dom.describeNode_(expectedNode) + errorSuffix, + actualNode); + + // Do no processing for expectedNode == div. + if (expectedNode == div) { + return; + } + + assertEquals( + 'Should have the same node type, got ' + + goog.testing.dom.describeNode_(actualNode) + ' but expected ' + + goog.testing.dom.describeNode_(expectedNode) + '.' + errorSuffix, + expectedNode.nodeType, actualNode.nodeType); + + if (expectedNode.nodeType == goog.dom.NodeType.ELEMENT) { + var expectedElem = goog.asserts.assertElement(expectedNode); + var actualElem = goog.asserts.assertElement(actualNode); + + assertEquals( + 'Tag names should match' + errorSuffix, expectedElem.tagName, + actualElem.tagName); + assertEquals( + 'Namespaces should match' + errorSuffix, expectedElem.namespaceURI, + actualElem.namespaceURI); + assertObjectEquals( + 'Should have same styles' + errorSuffix, + goog.style.parseStyleAttribute(expectedElem.style.cssText), + goog.style.parseStyleAttribute(actualElem.style.cssText)); + goog.testing.dom.assertAttributesEqual_( + errorSuffix, expectedElem, actualElem, !!opt_strictAttributes); + + if (IE_TEXT_COLLAPSE && + goog.style.getCascadedStyle(actualElem, 'display') != 'inline') { + // Text may be collapsed after any non-inline element. + collapsible = true; + } + + // Contents of template tags belong to a separate document and are not + // iterated on by the current iterator, unless the browser is too old to + // treat template tags differently. We recursively assert equality of the + // two template document fragments. + if (actualElem.tagName == goog.dom.TagName.TEMPLATE) { + // IE throws if HTMLTemplateElement is referenced at runtime. + actualElem = /** @type {HTMLTemplateElement} */ (actualElem); + if (actualElem.content) { + goog.testing.dom.assertHtmlMatches( + expectedElem.innerHTML, actualElem.innerHTML, + opt_strictAttributes); + } + } + } else { + // Concatenate text nodes until we reach a non text node. + var actualText = actualNode.nodeValue; + preIterated = true; + while ((actualNode = goog.iter.nextOrValue(actualIt, null)) && + actualNode.nodeType == goog.dom.NodeType.TEXT) { + actualText += actualNode.nodeValue; + } + + if (IE_TEXT_COLLAPSE) { + // Collapse the leading whitespace, unless the string consists entirely + // of whitespace. + if (collapsible && !goog.string.isEmptyOrWhitespace(actualText)) { + actualText = goog.string.trimLeft(actualText); + } + // Prepare to collapse whitespace in the next Text node if this one does + // not end in a whitespace character. + collapsible = /\s$/.test(actualText); + } + + var expectedText = goog.testing.dom.getExpectedText_(expectedNode); + if ((actualText && !goog.string.isBreakingWhitespace(actualText)) || + (expectedText && !goog.string.isBreakingWhitespace(expectedText))) { + var normalizedActual = actualText.replace(/\s+/g, ' '); + var normalizedExpected = expectedText.replace(/\s+/g, ' '); + + assertEquals( + 'Text should match' + errorSuffix, normalizedExpected, + normalizedActual); + } + } + + number++; + }); + + advanceActualNode(); + assertNull( + 'Finished expected HTML before finishing actual HTML' + errorSuffix, + goog.iter.nextOrValue(actualIt, null)); +}; + + +/** + * Assert that the html in `actual` is substantially similar to + * htmlPattern. This method tests for the same set of styles, and for the same + * order of nodes. Breaking whitespace nodes are ignored. Elements can be + * annotated with classnames corresponding to keys in goog.userAgent and will be + * expected to show up in that user agent and expected not to show up in + * others. + * @param {string} htmlPattern The pattern to match. + * @param {string} actual The html to check. + * @param {boolean=} opt_strictAttributes If false, attributes that appear in + * htmlPattern must be in actual, but actual can have attributes not + * present in htmlPattern. If true, htmlPattern and actual must have the + * same set of attributes. Default is false. + */ +goog.testing.dom.assertHtmlMatches = function( + htmlPattern, actual, opt_strictAttributes) { + var div = goog.dom.createDom(goog.dom.TagName.DIV); + div.innerHTML = actual; + + goog.testing.dom.assertHtmlContentsMatch( + htmlPattern, div, opt_strictAttributes); +}; + + +/** + * Finds the first text node descendant of root with the given content. Note + * that this operates on a text node level, so if text nodes get split this + * may not match the user visible text. Using normalize() may help here. + * @param {string|RegExp} textOrRegexp The text to find, or a regular + * expression to find a match of. + * @param {Element} root The element to search in. + * @return {?Node} The first text node that matches, or null if none is found. + */ +goog.testing.dom.findTextNode = function(textOrRegexp, root) { + var it = new goog.dom.NodeIterator(root); + var ret = goog.iter.nextOrValue(goog.iter.filter(it, function(node) { + if (node.nodeType == goog.dom.NodeType.TEXT) { + if (goog.isString(textOrRegexp)) { + return node.nodeValue == textOrRegexp; + } else { + return !!node.nodeValue.match(textOrRegexp); + } + } else { + return false; + } + }), null); + return ret; +}; + + +/** + * Assert the end points of a range. + * + * Notice that "Are two ranges visually identical?" and "Do two ranges have + * the same endpoint?" are independent questions. Two visually identical ranges + * may have different endpoints. And two ranges with the same endpoints may + * be visually different. + * + * @param {Node} start The expected start node. + * @param {number} startOffset The expected start offset. + * @param {Node} end The expected end node. + * @param {number} endOffset The expected end offset. + * @param {goog.dom.AbstractRange} range The actual range. + */ +goog.testing.dom.assertRangeEquals = function( + start, startOffset, end, endOffset, range) { + assertEquals('Unexpected start node', start, range.getStartNode()); + assertEquals('Unexpected end node', end, range.getEndNode()); + assertEquals('Unexpected start offset', startOffset, range.getStartOffset()); + assertEquals('Unexpected end offset', endOffset, range.getEndOffset()); +}; + + +/** + * Gets the value of a DOM attribute in deterministic way. + * @param {!Element} node A node. + * @param {string} name Attribute name. + * @return {*} Attribute value. + * @private + */ +goog.testing.dom.getAttributeValue_ = function(node, name) { + // These hacks avoid nondetermistic results in the following cases: + // WebKit: Two radio buttons with the same name can't be checked at the same + // time, even if only one of them is in the document. + if (goog.userAgent.WEBKIT && node.tagName == goog.dom.TagName.INPUT && + node['type'] == goog.dom.InputType.RADIO && name == 'checked') { + return false; + } + + // IE/Edge: cannot use node['src'] when the attribute contains HTTP + // credentials. getAttribute works though. + if ((goog.userAgent.IE || goog.userAgent.EDGE) && name == 'src') { + return node.getAttribute(name); + } + + // All browsers: some attributes return different values for getAttribute even + // if the values are semantically equivalent. E.g.
and + //
should register as equal. We use node[name] + // if it's available. + return node[name] !== undefined && + typeof node.getAttribute(name) != typeof node[name] ? + node[name] : + node.getAttribute(name); +}; + + +/** + * Assert that the attributes of two Nodes are the same (ignoring any + * instances of the style attribute). + * @param {string} errorSuffix String to add to end of error messages. + * @param {!Element} expectedElem The element whose attributes we are expecting. + * @param {!Element} actualElem The element with the actual attributes. + * @param {boolean} strictAttributes If false, attributes that appear in + * expectedNode must also be in actualNode, but actualNode can have + * attributes not present in expectedNode. If true, expectedNode and + * actualNode must have the same set of attributes. + * @private + */ +goog.testing.dom.assertAttributesEqual_ = function( + errorSuffix, expectedElem, actualElem, strictAttributes) { + if (strictAttributes) { + goog.testing.dom.compareClassAttribute_(expectedElem, actualElem); + } + + var expectedAttributes = expectedElem.attributes; + var actualAttributes = actualElem.attributes; + + for (var i = 0, len = expectedAttributes.length; i < len; i++) { + var expectedName = expectedAttributes[i].name; + var expectedValue = + goog.testing.dom.getAttributeValue_(expectedElem, expectedName); + + var actualAttribute = actualAttributes[expectedName]; + var actualValue = + goog.testing.dom.getAttributeValue_(actualElem, expectedName); + + // IE enumerates attribute names in the expected node that are not present, + // causing an undefined actualAttribute. + if (!expectedValue && !actualValue) { + continue; + } + + if (expectedName == 'id' && goog.userAgent.IE) { + goog.testing.dom.compareIdAttributeForIe_( + /** @type {string} */ (expectedValue), actualAttribute, + strictAttributes, errorSuffix); + continue; + } + + if (goog.testing.dom.ignoreAttribute_(expectedName)) { + continue; + } + + assertNotUndefined( + 'Expected to find attribute with name ' + expectedName + + ', in element ' + goog.testing.dom.describeNode_(actualElem) + + errorSuffix, + actualAttribute); + assertEquals( + 'Expected attribute ' + expectedName + ' has a different value ' + + errorSuffix, + String(expectedValue), String( + goog.testing.dom.getAttributeValue_( + actualElem, actualAttribute.name))); + } + + if (strictAttributes) { + for (i = 0; i < actualAttributes.length; i++) { + var actualName = actualAttributes[i].name; + var actualAttribute = actualAttributes.getNamedItem(actualName); + + if (!actualAttribute || goog.testing.dom.ignoreAttribute_(actualName)) { + continue; + } + + assertNotUndefined( + 'Unexpected attribute with name ' + actualName + ' in element ' + + goog.testing.dom.describeNode_(actualElem) + errorSuffix, + expectedAttributes[actualName]); + } + } +}; + + +/** + * Assert the class attribute of actualElem is the same as the one in + * expectedElem, ignoring classes that are useragents. + * @param {!Element} expectedElem The DOM element whose class we expect. + * @param {!Element} actualElem The DOM element with the actual class. + * @private + */ +goog.testing.dom.compareClassAttribute_ = function(expectedElem, actualElem) { + var classes = goog.dom.classlist.get(expectedElem); + + var expectedClasses = []; + for (var i = 0, len = classes.length; i < len; i++) { + if (!(classes[i] in goog.userAgent)) { + expectedClasses.push(classes[i]); + } + } + expectedClasses.sort(); + + var actualClasses = goog.array.toArray(goog.dom.classlist.get(actualElem)); + actualClasses.sort(); + + assertArrayEquals( + 'Expected class was: ' + expectedClasses.join(' ') + + ', but actual class was: ' + actualElem.className + ' in node ' + + goog.testing.dom.describeNode_(actualElem), + expectedClasses, actualClasses); +}; + + +/** + * Set of attributes IE adds to elements randomly. + * @type {Object} + * @private + */ +goog.testing.dom.BAD_IE_ATTRIBUTES_ = goog.object.createSet( + 'methods', 'CHECKED', 'dataFld', 'dataFormatAs', 'dataSrc'); + + +/** + * Whether to ignore the attribute. + * @param {string} name Name of the attribute. + * @return {boolean} True if the attribute should be ignored. + * @private + */ +goog.testing.dom.ignoreAttribute_ = function(name) { + if (name == 'style' || name == 'class' || name == 'xmlns') { + return true; + } + return goog.userAgent.IE && goog.testing.dom.BAD_IE_ATTRIBUTES_[name]; +}; + + +/** + * Compare id attributes for IE. In IE, if an element lacks an id attribute + * in the original HTML, the element object will still have such an attribute, + * but its value will be the empty string. + * @param {string} expectedValue The expected value of the id attribute. + * @param {Attr} actualAttribute The actual id attribute. + * @param {boolean} strictAttributes Whether strict attribute checking should be + * done. + * @param {string} errorSuffix String to append to error messages. + * @private + */ +goog.testing.dom.compareIdAttributeForIe_ = function( + expectedValue, actualAttribute, strictAttributes, errorSuffix) { + if (expectedValue === '') { + if (strictAttributes) { + assertTrue( + 'Unexpected attribute with name id in element ' + errorSuffix, + actualAttribute.value == ''); + } + } else { + assertNotUndefined( + 'Expected to find attribute with name id, in element ' + errorSuffix, + actualAttribute); + assertNotEquals( + 'Expected to find attribute with name id, in element ' + errorSuffix, + '', actualAttribute.value); + assertEquals( + 'Expected attribute has a different value ' + errorSuffix, + expectedValue, actualAttribute.value); + } +}; diff --git a/closure-library/closure/goog/testing/editor/dom.js b/closure-library/closure/goog/testing/editor/dom.js new file mode 100644 index 0000000000..097a57b902 --- /dev/null +++ b/closure-library/closure/goog/testing/editor/dom.js @@ -0,0 +1,300 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Testing utilities for editor specific DOM related tests. + * + */ + +goog.setTestOnly('goog.testing.editor.dom'); +goog.provide('goog.testing.editor.dom'); + +goog.require('goog.dom.AbstractRange'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.TagIterator'); +goog.require('goog.dom.TagWalkType'); +goog.require('goog.iter'); +goog.require('goog.string'); +goog.require('goog.testing.asserts'); + + +/** + * Returns the previous (in document order) node from the given node that is a + * non-empty text node, or null if none is found or opt_stopAt is not an + * ancestor of node. Note that if the given node has children, the search will + * start from the end tag of the node, meaning all its descendants will be + * included in the search, unless opt_skipDescendants is true. + * @param {Node} node Node to start searching from. + * @param {Node=} opt_stopAt Node to stop searching at (search will be + * restricted to this node's subtree), defaults to the body of the document + * containing node. + * @param {boolean=} opt_skipDescendants Whether to skip searching the given + * node's descentants. + * @return {Text} The previous (in document order) node from the given node + * that is a non-empty text node, or null if none is found. + */ +goog.testing.editor.dom.getPreviousNonEmptyTextNode = function( + node, opt_stopAt, opt_skipDescendants) { + return goog.testing.editor.dom.getPreviousNextNonEmptyTextNodeHelper_( + node, opt_stopAt, opt_skipDescendants, true); +}; + + +/** + * Returns the next (in document order) node from the given node that is a + * non-empty text node, or null if none is found or opt_stopAt is not an + * ancestor of node. Note that if the given node has children, the search will + * start from the start tag of the node, meaning all its descendants will be + * included in the search, unless opt_skipDescendants is true. + * @param {Node} node Node to start searching from. + * @param {Node=} opt_stopAt Node to stop searching at (search will be + * restricted to this node's subtree), defaults to the body of the document + * containing node. + * @param {boolean=} opt_skipDescendants Whether to skip searching the given + * node's descentants. + * @return {Text} The next (in document order) node from the given node that + * is a non-empty text node, or null if none is found or opt_stopAt is not + * an ancestor of node. + */ +goog.testing.editor.dom.getNextNonEmptyTextNode = function( + node, opt_stopAt, opt_skipDescendants) { + return goog.testing.editor.dom.getPreviousNextNonEmptyTextNodeHelper_( + node, opt_stopAt, opt_skipDescendants, false); +}; + + +/** + * Helper that returns the previous or next (in document order) node from the + * given node that is a non-empty text node, or null if none is found or + * opt_stopAt is not an ancestor of node. Note that if the given node has + * children, the search will start from the end or start tag of the node + * (depending on whether it's searching for the previous or next node), meaning + * all its descendants will be included in the search, unless + * opt_skipDescendants is true. + * @param {Node} node Node to start searching from. + * @param {Node=} opt_stopAt Node to stop searching at (search will be + * restricted to this node's subtree), defaults to the body of the document + * containing node. + * @param {boolean=} opt_skipDescendants Whether to skip searching the given + * node's descentants. + * @param {boolean=} opt_isPrevious Whether to search for the previous non-empty + * text node instead of the next one. + * @return {Text} The next (in document order) node from the given node that + * is a non-empty text node, or null if none is found or opt_stopAt is not + * an ancestor of node. + * @private + */ +goog.testing.editor.dom.getPreviousNextNonEmptyTextNodeHelper_ = function( + node, opt_stopAt, opt_skipDescendants, opt_isPrevious) { + opt_stopAt = opt_stopAt || node.ownerDocument.body; + // Initializing the iterator to iterate over the children of opt_stopAt + // makes it stop only when it finishes iterating through all of that + // node's children, even though we will start at a different node and exit + // that starting node's subtree in the process. + var iter = new goog.dom.TagIterator(opt_stopAt, opt_isPrevious); + + // TODO(user): Move this logic to a new method in TagIterator such as + // skipToNode(). + // Then we set the iterator to start at the given start node, not opt_stopAt. + var walkType; // Let TagIterator set the initial walk type by default. + var depth = goog.testing.editor.dom.getRelativeDepth_(node, opt_stopAt); + if (depth == -1) { + return null; // Fail because opt_stopAt is not an ancestor of node. + } + if (node.nodeType == goog.dom.NodeType.ELEMENT) { + if (opt_skipDescendants) { + // Specifically set the initial walk type so that we skip the descendant + // subtree by starting at the start if going backwards or at the end if + // going forwards. + walkType = opt_isPrevious ? goog.dom.TagWalkType.START_TAG : + goog.dom.TagWalkType.END_TAG; + } else { + // We're starting "inside" an element node so the depth needs to be one + // deeper than the node's actual depth. That's how TagIterator works! + depth++; + } + } + iter.setPosition(node, walkType, depth); + + // Advance the iterator so it skips the start node. + try { + iter.next(); + } catch (e) { + return null; // It could have been a leaf node. + } + // Now just get the first non-empty text node the iterator finds. + var filter = + goog.iter.filter(iter, goog.testing.editor.dom.isNonEmptyTextNode_); + try { + return /** @type {Text} */ (filter.next()); + } catch (e) { // No next item is available so return null. + return null; + } +}; + + +/** + * Returns whether the given node is a non-empty text node. + * @param {Node} node Node to be checked. + * @return {boolean} Whether the given node is a non-empty text node. + * @private + */ +goog.testing.editor.dom.isNonEmptyTextNode_ = function(node) { + if (node && node.nodeType == goog.dom.NodeType.TEXT) { + node = /** @type {!Text} */ (node); + return node.length > 0; + } + + return false; +}; + + +/** + * Returns the depth of the given node relative to the given parent node, or -1 + * if the given node is not a descendant of the given parent node. E.g. if + * node == parentNode returns 0, if node.parentNode == parentNode returns 1, + * etc. + * @param {Node} node Node whose depth to get. + * @param {Node} parentNode Node relative to which the depth should be + * calculated. + * @return {number} The depth of the given node relative to the given parent + * node, or -1 if the given node is not a descendant of the given parent + * node. + * @private + */ +goog.testing.editor.dom.getRelativeDepth_ = function(node, parentNode) { + var depth = 0; + while (node) { + if (node == parentNode) { + return depth; + } + node = node.parentNode; + depth++; + } + return -1; +}; + + +/** + * Assert that the range is surrounded by the given strings. This is useful + * because different browsers can place the range endpoints inside different + * nodes even when visually the range looks the same. Also, there may be empty + * text nodes in the way (again depending on the browser) making it difficult to + * use assertRangeEquals. + * @param {string} before String that should occur immediately before the start + * point of the range. If this is the empty string, assert will only succeed + * if there is no text before the start point of the range. + * @param {string} after String that should occur immediately after the end + * point of the range. If this is the empty string, assert will only succeed + * if there is no text after the end point of the range. + * @param {goog.dom.AbstractRange} range The range to be tested. + * @param {Node=} opt_stopAt Node to stop searching at (search will be + * restricted to this node's subtree). + */ +goog.testing.editor.dom.assertRangeBetweenText = function( + before, after, range, opt_stopAt) { + var previousText = + goog.testing.editor.dom.getTextFollowingRange_(range, true, opt_stopAt); + if (before == '') { + assertNull( + 'Expected nothing before range but found <' + previousText + '>', + previousText); + } else { + assertNotNull( + 'Expected <' + before + '> before range but found nothing', + previousText); + assertTrue( + 'Expected <' + before + '> before range but found <' + previousText + + '>', + goog.string.endsWith( + /** @type {string} */ (previousText), before)); + } + var nextText = + goog.testing.editor.dom.getTextFollowingRange_(range, false, opt_stopAt); + if (after == '') { + assertNull( + 'Expected nothing after range but found <' + nextText + '>', nextText); + } else { + assertNotNull( + 'Expected <' + after + '> after range but found nothing', nextText); + assertTrue( + 'Expected <' + after + '> after range but found <' + nextText + '>', + goog.string.startsWith( + /** @type {string} */ (nextText), after)); + } +}; + + +/** + * Returns the text that follows the given range, where the term "follows" means + * "comes immediately before the start of the range" if isBefore is true, and + * "comes immediately after the end of the range" if isBefore is false, or null + * if no non-empty text node is found. + * @param {goog.dom.AbstractRange} range The range to search from. + * @param {boolean} isBefore Whether to search before the range instead of + * after it. + * @param {Node=} opt_stopAt Node to stop searching at (search will be + * restricted to this node's subtree). + * @return {?string} The text that follows the given range, or null if no + * non-empty text node is found. + * @private + */ +goog.testing.editor.dom.getTextFollowingRange_ = function( + range, isBefore, opt_stopAt) { + var followingTextNode; + var endpointNode = isBefore ? range.getStartNode() : range.getEndNode(); + var endpointOffset = isBefore ? range.getStartOffset() : range.getEndOffset(); + var getFollowingTextNode = isBefore ? + goog.testing.editor.dom.getPreviousNonEmptyTextNode : + goog.testing.editor.dom.getNextNonEmptyTextNode; + + if (endpointNode.nodeType == goog.dom.NodeType.TEXT) { + // Range endpoint is in a text node. + var endText = endpointNode.nodeValue; + if (isBefore ? endpointOffset > 0 : endpointOffset < endText.length) { + // There is text in this node following the endpoint so return the portion + // that follows the endpoint. + return isBefore ? endText.substr(0, endpointOffset) : + endText.substr(endpointOffset); + } else { + // There is no text following the endpoint so look for the follwing text + // node. + followingTextNode = getFollowingTextNode(endpointNode, opt_stopAt); + return followingTextNode && followingTextNode.nodeValue; + } + } else { + // Range endpoint is in an element node. + var numChildren = endpointNode.childNodes.length; + if (isBefore ? endpointOffset > 0 : endpointOffset < numChildren) { + // There is at least one child following the endpoint. + var followingChild = + endpointNode + .childNodes[isBefore ? endpointOffset - 1 : endpointOffset]; + if (goog.testing.editor.dom.isNonEmptyTextNode_(followingChild)) { + // The following child has text so return that. + return followingChild.nodeValue; + } else { + // The following child has no text so look for the following text node. + followingTextNode = getFollowingTextNode(followingChild, opt_stopAt); + return followingTextNode && followingTextNode.nodeValue; + } + } else { + // There is no child following the endpoint, so search from the endpoint + // node, but don't search its children because they are not following the + // endpoint! + followingTextNode = getFollowingTextNode(endpointNode, opt_stopAt, true); + return followingTextNode && followingTextNode.nodeValue; + } + } +}; diff --git a/closure-library/closure/goog/testing/editor/fieldmock.js b/closure-library/closure/goog/testing/editor/fieldmock.js new file mode 100644 index 0000000000..1b1ef61b57 --- /dev/null +++ b/closure-library/closure/goog/testing/editor/fieldmock.js @@ -0,0 +1,113 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Mock of goog.editor.field. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.setTestOnly('goog.testing.editor.FieldMock'); +goog.provide('goog.testing.editor.FieldMock'); + +goog.require('goog.dom'); +goog.require('goog.dom.Range'); +goog.require('goog.editor.Field'); +goog.require('goog.testing.LooseMock'); +goog.require('goog.testing.mockmatchers'); + + + +/** + * Mock of goog.editor.Field. + * @param {Window=} opt_window Window the field would edit. Defaults to + * `window`. + * @param {Window=} opt_appWindow "AppWindow" of the field, which can be + * different from `opt_window` when mocking a field that uses an + * iframe. Defaults to `opt_window`. + * @param {goog.dom.AbstractRange=} opt_range An object (mock or real) to be + * returned by getRange(). If omitted, a new goog.dom.Range is created + * from the window every time getRange() is called. + * @constructor + * @extends {goog.testing.LooseMock} + * @suppress {missingProperties} Mocks do not fit in the type system well. + * @final + */ +goog.testing.editor.FieldMock = function(opt_window, opt_appWindow, opt_range) { + goog.testing.LooseMock.call(this, goog.editor.Field); + opt_window = opt_window || window; + opt_appWindow = opt_appWindow || opt_window; + + // We want to pretend this is a Field even though it can't actaully be a + // subclass. + var thisField = /** @type {!goog.editor.Field} */ (/** @type {*} */ (this)); + + thisField.getAppWindow(); + this.$anyTimes(); + this.$returns(opt_appWindow); + + thisField.getRange(); + this.$anyTimes(); + this.$does(function() { + return opt_range || goog.dom.Range.createFromWindow(opt_window); + }); + + thisField.getEditableDomHelper(); + this.$anyTimes(); + this.$returns(goog.dom.getDomHelper(opt_window.document)); + + thisField.usesIframe(); + this.$anyTimes(); + + thisField.getBaseZindex(); + this.$anyTimes(); + this.$returns(0); + + thisField.restoreSavedRange( + /** @type {?} */ (goog.testing.mockmatchers.ignoreArgument)); + this.$anyTimes(); + this.$does(function(range) { + if (range) { + range.restore(); + } + thisField.focus(); + }); + + // These methods cannot be set on the prototype, because the prototype + // gets stepped on by the mock framework. + var inModalMode = false; + + /** + * @return {boolean} Whether we're in modal interaction mode. + */ + this.inModalMode = function() { return inModalMode; }; + + /** + * @param {boolean} mode Sets whether we're in modal interaction mode. + */ + this.setModalMode = function(mode) { inModalMode = mode; }; + + var uneditable = false; + + /** + * @return {boolean} Whether the field is uneditable. + */ + this.isUneditable = function() { return uneditable; }; + + /** + * @param {boolean} isUneditable Whether the field is uneditable. + */ + this.setUneditable = function(isUneditable) { uneditable = isUneditable; }; +}; +goog.inherits(goog.testing.editor.FieldMock, goog.testing.LooseMock); diff --git a/closure-library/closure/goog/testing/editor/testhelper.js b/closure-library/closure/goog/testing/editor/testhelper.js new file mode 100644 index 0000000000..68b903f9c2 --- /dev/null +++ b/closure-library/closure/goog/testing/editor/testhelper.js @@ -0,0 +1,185 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Class that allows for simple text editing tests. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.setTestOnly('goog.testing.editor.TestHelper'); +goog.provide('goog.testing.editor.TestHelper'); + +goog.require('goog.Disposable'); +goog.require('goog.dom'); +goog.require('goog.dom.Range'); +goog.require('goog.editor.BrowserFeature'); +goog.require('goog.editor.node'); +goog.require('goog.editor.plugins.AbstractBubblePlugin'); +goog.require('goog.testing.dom'); + + + +/** + * Create a new test controller. + * @param {Element} root The root editable element. + * @constructor + * @extends {goog.Disposable} + * @final + */ +goog.testing.editor.TestHelper = function(root) { + if (!root) { + throw new Error('Null root'); + } + goog.Disposable.call(this); + + /** + * Convenience variable for root DOM element. + * @type {!Element} + * @private + */ + this.root_ = root; + + /** + * The starting HTML of the editable element. + * @type {string} + * @private + */ + this.savedHtml_ = ''; +}; +goog.inherits(goog.testing.editor.TestHelper, goog.Disposable); + + +/** + * Selects a new root element. + * @param {Element} root The root editable element. + */ +goog.testing.editor.TestHelper.prototype.setRoot = function(root) { + if (!root) { + throw new Error('Null root'); + } + this.root_ = root; +}; + + +/** + * Make the root element editable. Also saves its HTML to be restored + * in tearDown. + */ +goog.testing.editor.TestHelper.prototype.setUpEditableElement = function() { + this.savedHtml_ = this.root_.innerHTML; + if (goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) { + this.root_.contentEditable = true; + } else { + this.root_.ownerDocument.designMode = 'on'; + } + this.root_.setAttribute('g_editable', 'true'); +}; + + +/** + * Reset the element previously initialized, restoring its HTML and making it + * non editable. + * @suppress {accessControls} Private state of + * {@link goog.editor.plugins.AbstractBubblePlugin} is accessed for test + * purposes. + */ +goog.testing.editor.TestHelper.prototype.tearDownEditableElement = function() { + if (goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) { + this.root_.contentEditable = false; + } else { + this.root_.ownerDocument.designMode = 'off'; + } + goog.dom.removeChildren(this.root_); + this.root_.innerHTML = this.savedHtml_; + this.root_.removeAttribute('g_editable'); + + if (goog.editor.plugins && goog.editor.plugins.AbstractBubblePlugin) { + // Remove old bubbles. + for (var key in goog.editor.plugins.AbstractBubblePlugin.bubbleMap_) { + goog.editor.plugins.AbstractBubblePlugin.bubbleMap_[key].dispose(); + } + // Ensure we get a new bubble for each test. + goog.editor.plugins.AbstractBubblePlugin.bubbleMap_ = {}; + } +}; + + +/** + * Assert that the html in 'root' is substantially similar to htmlPattern. + * This method tests for the same set of styles, and for the same order of + * nodes. Breaking whitespace nodes are ignored. Elements can be annotated + * with classnames corresponding to keys in goog.userAgent and will be + * expected to show up in that user agent and expected not to show up in + * others. + * @param {string} htmlPattern The pattern to match. + */ +goog.testing.editor.TestHelper.prototype.assertHtmlMatches = function( + htmlPattern) { + goog.testing.dom.assertHtmlContentsMatch(htmlPattern, this.root_); +}; + + +/** + * Finds the first text node descendant of root with the given content. + * @param {string|RegExp} textOrRegexp The text to find, or a regular + * expression to find a match of. + * @return {Node} The first text node that matches, or null if none is found. + */ +goog.testing.editor.TestHelper.prototype.findTextNode = function(textOrRegexp) { + return goog.testing.dom.findTextNode(textOrRegexp, this.root_); +}; + + +/** + * Select from the given `fromOffset` in the given `from` node to + * the given `toOffset` in the optionally given `to` node. If nodes + * are passed in, uses them, otherwise uses findTextNode to find the nodes to + * select. Selects a caret if opt_to and opt_toOffset are not given. + * @param {Node|string} from Node or text of the node to start the selection at. + * @param {number} fromOffset Offset within the above node to start the + * selection at. + * @param {Node|string=} opt_to Node or text of the node to end the selection + * at. + * @param {number=} opt_toOffset Offset within the above node to end the + * selection at. + * @return {!goog.dom.AbstractRange} + */ +goog.testing.editor.TestHelper.prototype.select = function( + from, fromOffset, opt_to, opt_toOffset) { + var end; + var start = end = goog.isString(from) ? this.findTextNode(from) : from; + var endOffset; + var startOffset = endOffset = fromOffset; + + if (opt_to && goog.isNumber(opt_toOffset)) { + end = goog.isString(opt_to) ? this.findTextNode(opt_to) : opt_to; + endOffset = opt_toOffset; + } + + var range = + goog.dom.Range.createFromNodes(start, startOffset, end, endOffset); + range.select(); + return range; +}; + + +/** @override */ +goog.testing.editor.TestHelper.prototype.disposeInternal = function() { + if (goog.editor.node.isEditableContainer(this.root_)) { + this.tearDownEditableElement(); + } + delete this.root_; + goog.testing.editor.TestHelper.base(this, 'disposeInternal'); +}; diff --git a/closure-library/closure/goog/testing/events/eventobserver.js b/closure-library/closure/goog/testing/events/eventobserver.js new file mode 100644 index 0000000000..cc855e5673 --- /dev/null +++ b/closure-library/closure/goog/testing/events/eventobserver.js @@ -0,0 +1,94 @@ +// Copyright 2010 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Event observer. + * + * Provides an event observer that holds onto events that it handles. This + * can be used in unit testing to verify an event target's events -- + * that the order count, types, etc. are correct. + * + * Example usage: + *
+ * var observer = new goog.testing.events.EventObserver();
+ * var widget = new foo.Widget();
+ * goog.events.listen(widget, ['select', 'submit'], observer);
+ * // Simulate user action of 3 select events and 2 submit events.
+ * assertEquals(3, observer.getEvents('select').length);
+ * assertEquals(2, observer.getEvents('submit').length);
+ * 
+ * + * @author nnaze@google.com (Nathan Naze) + */ + +goog.setTestOnly('goog.testing.events.EventObserver'); +goog.provide('goog.testing.events.EventObserver'); + +goog.require('goog.array'); +goog.require('goog.events.Event'); + + + +/** + * Event observer. Implements a handleEvent interface so it may be used as + * a listener in listening functions and methods. + * @see goog.events.listen + * @see goog.events.EventHandler + * @constructor + * @final + */ +goog.testing.events.EventObserver = function() { + + /** + * A list of events handled by the observer in order of handling, oldest to + * newest. + * @type {!Array} + * @private + */ + this.events_ = []; +}; + + +/** + * Handles an event and remembers it. Event listening functions and methods + * will call this method when this observer is used as a listener. + * @see goog.events.listen + * @see goog.events.EventHandler + * @param {!goog.events.Event} e Event to handle. + */ +goog.testing.events.EventObserver.prototype.handleEvent = function(e) { + this.events_.push(e); +}; + + +/** + * @param {string=} opt_type If given, only return events of this type. + * @return {!Array} The events handled, oldest to newest. + */ +goog.testing.events.EventObserver.prototype.getEvents = function(opt_type) { + var events = goog.array.clone(this.events_); + + if (opt_type) { + events = goog.array.filter( + events, function(event) { return event.type == opt_type; }); + } + + return events; +}; + + +/** Clears the list of events seen by this observer. */ +goog.testing.events.EventObserver.prototype.clear = function() { + this.events_ = []; +}; diff --git a/closure-library/closure/goog/testing/events/events.js b/closure-library/closure/goog/testing/events/events.js new file mode 100644 index 0000000000..f0a4cf393b --- /dev/null +++ b/closure-library/closure/goog/testing/events/events.js @@ -0,0 +1,891 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Event Simulation. + * + * Utility functions for simulating events at the Closure level. All functions + * in this package generate events by calling goog.events.fireListeners, + * rather than interfacing with the browser directly. This is intended for + * testing purposes, and should not be used in production code. + * + * The decision to use Closure events and dispatchers instead of the browser's + * native events and dispatchers was conscious and deliberate. Native event + * dispatchers have their own set of quirks and edge cases. Pure JS dispatchers + * are more robust and transparent. + * + * If you think you need a testing mechanism that uses native Event objects, + * please, please email closure-tech first to explain your use case before you + * sink time into this. + * + * TODO(b/8933952): Migrate to explicitly non-nullable types. At present, many + * functions in this file expect non-null inputs but do not explicitly + * indicate this. + * + * @author nicksantos@google.com (Nick Santos) + */ + +goog.setTestOnly('goog.testing.events'); +goog.provide('goog.testing.events'); +goog.provide('goog.testing.events.Event'); + +goog.require('goog.Disposable'); +goog.require('goog.asserts'); +goog.require('goog.dom.NodeType'); +goog.require('goog.events'); +goog.require('goog.events.BrowserEvent'); +goog.require('goog.events.BrowserFeature'); +goog.require('goog.events.EventTarget'); +goog.require('goog.events.EventType'); +goog.require('goog.events.KeyCodes'); +goog.require('goog.object'); +goog.require('goog.style'); +goog.require('goog.userAgent'); + + + +/** + * goog.events.BrowserEvent expects an Event so we provide one for JSCompiler. + * + * This clones a lot of the functionality of goog.events.Event. This used to + * use a mixin, but the mixin results in confusing the two types when compiled. + * + * @param {string} type Event Type. + * @param {Object=} opt_target Reference to the object that is the target of + * this event. + * @constructor + * @extends {Event} + */ +goog.testing.events.Event = function(type, opt_target) { + this.type = type; + + this.target = /** @type {EventTarget} */ (opt_target || null); + + this.currentTarget = this.target; +}; + + +/** + * Whether to cancel the event in internal capture/bubble processing for IE. + * @type {boolean} + * @public + * @suppress {underscore|visibility} Technically public, but referencing this + * outside this package is strongly discouraged. + */ +goog.testing.events.Event.prototype.propagationStopped_ = false; + + +/** @override */ +goog.testing.events.Event.prototype.defaultPrevented = false; + + +/** + * Return value for in internal capture/bubble processing for IE. + * @type {boolean} + * @public + * @suppress {underscore|visibility} Technically public, but referencing this + * outside this package is strongly discouraged. + */ +goog.testing.events.Event.prototype.returnValue_ = true; + + +/** @override */ +goog.testing.events.Event.prototype.stopPropagation = function() { + this.propagationStopped_ = true; +}; + + +/** @override */ +goog.testing.events.Event.prototype.preventDefault = function() { + this.defaultPrevented = true; + this.returnValue_ = false; +}; + +/** + * Asserts an event target exists. This will fail if target is not defined. + * + * TODO(nnaze): Gradually add this to the methods in this file, and eventually + * update the method signatures to not take nullables. See + * http://b/8961907 + * + * @param {EventTarget} target A target to assert. + * @return {!EventTarget} The target, guaranteed to exist. + * @private + */ +goog.testing.events.assertEventTarget_ = function(target) { + return goog.asserts.assert(target, 'EventTarget should be defined.'); +}; + + +/** + * A static helper function that sets the mouse position to the event. + * @param {Event} event A simulated native event. + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's + * target's position (if available), otherwise (0, 0). + * @private + */ +goog.testing.events.setEventClientXY_ = function(event, opt_coords) { + if (!opt_coords && event.target && + /** @type {!Node} */ (event.target).nodeType == + goog.dom.NodeType.ELEMENT) { + try { + opt_coords = goog.style.getClientPosition( + /** @type {!Element} **/ (event.target)); + } catch (ex) { + // IE sometimes throws if it can't get the position. + } + } + event.clientX = opt_coords ? opt_coords.x : 0; + event.clientY = opt_coords ? opt_coords.y : 0; + + // Pretend the browser window is at (0, 0). + event.screenX = event.clientX; + event.screenY = event.clientY; +}; + + +/** + * Simulates a mousedown, mouseup, and then click on the given event target, + * with the left mouse button. + * @param {EventTarget} target The target for the event. + * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button; + * defaults to `goog.events.BrowserEvent.MouseButton.LEFT`. + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's + * target's position (if available), otherwise (0, 0). + * @param {Object=} opt_eventProperties Event properties to be mixed into the + * BrowserEvent. + * @return {boolean} The returnValue of the sequence: false if preventDefault() + * was called on any of the events, true otherwise. + */ +goog.testing.events.fireClickSequence = function( + target, opt_button, opt_coords, opt_eventProperties) { + // Fire mousedown, mouseup, and click. Then return the bitwise AND of the 3. + return goog.testing.events.eagerAnd_( + goog.testing.events.fireMouseDownEvent( + target, opt_button, opt_coords, opt_eventProperties), + goog.testing.events.fireMouseUpEvent( + target, opt_button, opt_coords, opt_eventProperties), + goog.testing.events.fireClickEvent( + target, opt_button, opt_coords, opt_eventProperties)); +}; + + +/** + * Simulates the sequence of events fired by the browser when the user double- + * clicks the given target. + * @param {EventTarget} target The target for the event. + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's + * target's position (if available), otherwise (0, 0). + * @param {Object=} opt_eventProperties Event properties to be mixed into the + * BrowserEvent. + * @return {boolean} The returnValue of the sequence: false if preventDefault() + * was called on any of the events, true otherwise. + */ +goog.testing.events.fireDoubleClickSequence = function( + target, opt_coords, opt_eventProperties) { + // Fire mousedown, mouseup, click, mousedown, mouseup, click, dblclick. + // Then return the bitwise AND of the 7. + var btn = goog.events.BrowserEvent.MouseButton.LEFT; + return goog.testing.events.eagerAnd_( + goog.testing.events.fireMouseDownEvent( + target, btn, opt_coords, opt_eventProperties), + goog.testing.events.fireMouseUpEvent( + target, btn, opt_coords, opt_eventProperties), + goog.testing.events.fireClickEvent( + target, btn, opt_coords, opt_eventProperties), + // IE fires a selectstart instead of the second mousedown in a + // dblclick, but we don't care about selectstart. + (goog.userAgent.IE || + goog.testing.events.fireMouseDownEvent( + target, btn, opt_coords, opt_eventProperties)), + goog.testing.events.fireMouseUpEvent( + target, btn, opt_coords, opt_eventProperties), + // IE doesn't fire the second click in a dblclick. + (goog.userAgent.IE || + goog.testing.events.fireClickEvent( + target, btn, opt_coords, opt_eventProperties)), + goog.testing.events.fireDoubleClickEvent( + target, opt_coords, opt_eventProperties)); +}; + + +/** + * A non-exhaustive mapping of keys to keyCode. These are not localized and + * are specific to QWERTY keyboards, but are used to augment our testing key + * events as much as possible in order to simulate real browser events. This + * will be used to fill out the `keyCode` field for key events when the `key` + * value is present in this map. + * @private {!Object} + * @final + */ +goog.testing.events.KEY_TO_KEYCODE_MAPPING_ = { + '0': goog.events.KeyCodes.ZERO, + '1': goog.events.KeyCodes.ONE, + '2': goog.events.KeyCodes.TWO, + '3': goog.events.KeyCodes.THREE, + '4': goog.events.KeyCodes.FOUR, + '5': goog.events.KeyCodes.FIVE, + '6': goog.events.KeyCodes.SIX, + '7': goog.events.KeyCodes.SEVEN, + '8': goog.events.KeyCodes.EIGHT, + '9': goog.events.KeyCodes.NINE, + 'a': goog.events.KeyCodes.A, + 'b': goog.events.KeyCodes.B, + 'c': goog.events.KeyCodes.C, + 'd': goog.events.KeyCodes.D, + 'e': goog.events.KeyCodes.E, + 'f': goog.events.KeyCodes.F, + 'g': goog.events.KeyCodes.G, + 'h': goog.events.KeyCodes.H, + 'i': goog.events.KeyCodes.I, + 'j': goog.events.KeyCodes.J, + 'k': goog.events.KeyCodes.K, + 'l': goog.events.KeyCodes.L, + 'm': goog.events.KeyCodes.M, + 'n': goog.events.KeyCodes.N, + 'o': goog.events.KeyCodes.O, + 'p': goog.events.KeyCodes.P, + 'q': goog.events.KeyCodes.Q, + 'r': goog.events.KeyCodes.R, + 's': goog.events.KeyCodes.S, + 't': goog.events.KeyCodes.T, + 'u': goog.events.KeyCodes.U, + 'v': goog.events.KeyCodes.V, + 'w': goog.events.KeyCodes.W, + 'x': goog.events.KeyCodes.X, + 'y': goog.events.KeyCodes.Y, + 'z': goog.events.KeyCodes.Z +}; + + +/** + * Simulates a complete keystroke (keydown, keypress, and keyup). Note that + * if preventDefault is called on the keydown, the keypress will not fire. + * + * @param {EventTarget} target The target for the event. + * @param {string|number} keyOrKeyCode The key value or keycode of the key + * pressed. + * @param {Object=} opt_eventProperties Event properties to be mixed into the + * BrowserEvent. + * @return {boolean} The returnValue of the sequence: false if preventDefault() + * was called on any of the events, true otherwise. + */ +goog.testing.events.fireKeySequence = function( + target, keyOrKeyCode, opt_eventProperties) { + return goog.testing.events.fireNonAsciiKeySequence( + target, keyOrKeyCode, keyOrKeyCode, opt_eventProperties); +}; + + +/** + * Simulates a complete keystroke (keydown, keypress, and keyup) when typing + * a non-ASCII character. Same as fireKeySequence, the keypress will not fire + * if preventDefault is called on the keydown. + * + * @param {EventTarget} target The target for the event. + * @param {string|number} keyOrKeyCode The key value or keycode of the keydown + * and keyup events. + * @param {string|number} keyPressKeyOrKeyCode The key value or keycode of the + * keypress event. + * @param {Object=} opt_eventProperties Event properties to be mixed into the + * BrowserEvent. + * @return {boolean} The returnValue of the sequence: false if preventDefault() + * was called on any of the events, true otherwise. + */ +goog.testing.events.fireNonAsciiKeySequence = function( + target, keyOrKeyCode, keyPressKeyOrKeyCode, opt_eventProperties) { + var keydown = + /** @type {!KeyboardEvent} */ ( + /** @type {!Event} */ (new goog.testing.events.Event( + goog.events.EventType.KEYDOWN, target))); + var keyup = // + /** @type {!KeyboardEvent} */ ( + /** @type {!Event} */ (new goog.testing.events.Event( + goog.events.EventType.KEYUP, target))); + var keypress = + /** @type {!KeyboardEvent} */ ( + /** @type {!Event} */ (new goog.testing.events.Event( + goog.events.EventType.KEYPRESS, target))); + + if (goog.isString(keyOrKeyCode)) { + keydown.key = keyup.key = /** @type {string} */ (keyOrKeyCode); + keypress.key = /** @type {string} */ (keyPressKeyOrKeyCode); + + // Try to fill the keyCode field for the key events if we have a known key. + // This is to try and make these mock simulated event as close to real + // browser events as possible. + var mappedKeyCode = + goog.testing.events + .KEY_TO_KEYCODE_MAPPING_[/** @type {string} */ (keyOrKeyCode) + .toLowerCase()]; + if (mappedKeyCode) { + keydown.keyCode = keyup.keyCode = mappedKeyCode; + } + + var mappedKeyPressKeyCode = + goog.testing.events.KEY_TO_KEYCODE_MAPPING_[/** @type {string} */ ( + keyPressKeyOrKeyCode) + .toLowerCase()]; + if (mappedKeyPressKeyCode) { + keypress.keyCode = mappedKeyPressKeyCode; + } + } else { + keydown.keyCode = keyup.keyCode = /** @type {number} */ (keyOrKeyCode); + keypress.keyCode = /** @type {number} */ (keyPressKeyOrKeyCode); + } + + if (opt_eventProperties) { + goog.object.extend(keydown, opt_eventProperties); + goog.object.extend(keyup, opt_eventProperties); + goog.object.extend(keypress, opt_eventProperties); + } + + // Fire keydown, keypress, and keyup. Note that if the keydown is + // prevent-defaulted, then the keypress will not fire. + var result = goog.testing.events.fireBrowserEvent(keydown); + if (goog.isString(keyOrKeyCode)) { + if (/** @type {string} */ (keyPressKeyOrKeyCode) != '' && result) { + result = goog.testing.events.eagerAnd_( + result, goog.testing.events.fireBrowserEvent(keypress)); + } + } else { + if (goog.events.KeyCodes.firesKeyPressEvent( + /** @type {number} */ (keyOrKeyCode), undefined, keydown.shiftKey, + keydown.ctrlKey, keydown.altKey, keydown.metaKey) && + result) { + result = goog.testing.events.eagerAnd_( + result, goog.testing.events.fireBrowserEvent(keypress)); + } + } + return goog.testing.events.eagerAnd_( + result, goog.testing.events.fireBrowserEvent(keyup)); +}; + + +/** + * Simulates a mouseenter event on the given target. + * @param {!EventTarget} target The target for the event. + * @param {?EventTarget} relatedTarget The related target for the event (e.g., + * the node that the mouse is being moved out of). + * @param {!goog.math.Coordinate=} opt_coords Mouse position. Defaults to + * event's target's position (if available), otherwise (0, 0). + * @return {boolean} The returnValue of the event: false if preventDefault() was + * called on it, true otherwise. + */ +goog.testing.events.fireMouseEnterEvent = function( + target, relatedTarget, opt_coords) { + var mouseenter = + new goog.testing.events.Event(goog.events.EventType.MOUSEENTER, target); + mouseenter.relatedTarget = relatedTarget; + goog.testing.events.setEventClientXY_(mouseenter, opt_coords); + return goog.testing.events.fireBrowserEvent(mouseenter); +}; + + +/** + * Simulates a mouseleave event on the given target. + * @param {!EventTarget} target The target for the event. + * @param {?EventTarget} relatedTarget The related target for the event (e.g., + * the node that the mouse is being moved into). + * @param {!goog.math.Coordinate=} opt_coords Mouse position. Defaults to + * event's target's position (if available), otherwise (0, 0). + * @return {boolean} The returnValue of the event: false if preventDefault() was + * called on it, true otherwise. + */ +goog.testing.events.fireMouseLeaveEvent = function( + target, relatedTarget, opt_coords) { + var mouseleave = + new goog.testing.events.Event(goog.events.EventType.MOUSELEAVE, target); + mouseleave.relatedTarget = relatedTarget; + goog.testing.events.setEventClientXY_(mouseleave, opt_coords); + return goog.testing.events.fireBrowserEvent(mouseleave); +}; + + +/** + * Simulates a mouseover event on the given target. + * @param {EventTarget} target The target for the event. + * @param {EventTarget} relatedTarget The related target for the event (e.g., + * the node that the mouse is being moved out of). + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's + * target's position (if available), otherwise (0, 0). + * @return {boolean} The returnValue of the event: false if preventDefault() was + * called on it, true otherwise. + */ +goog.testing.events.fireMouseOverEvent = function( + target, relatedTarget, opt_coords) { + var mouseover = + new goog.testing.events.Event(goog.events.EventType.MOUSEOVER, target); + mouseover.relatedTarget = relatedTarget; + goog.testing.events.setEventClientXY_(mouseover, opt_coords); + return goog.testing.events.fireBrowserEvent(mouseover); +}; + + +/** + * Simulates a mousemove event on the given target. + * @param {EventTarget} target The target for the event. + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's + * target's position (if available), otherwise (0, 0). + * @return {boolean} The returnValue of the event: false if preventDefault() was + * called on it, true otherwise. + */ +goog.testing.events.fireMouseMoveEvent = function(target, opt_coords) { + var mousemove = + new goog.testing.events.Event(goog.events.EventType.MOUSEMOVE, target); + + goog.testing.events.setEventClientXY_(mousemove, opt_coords); + return goog.testing.events.fireBrowserEvent(mousemove); +}; + + +/** + * Simulates a mouseout event on the given target. + * @param {EventTarget} target The target for the event. + * @param {EventTarget} relatedTarget The related target for the event (e.g., + * the node that the mouse is being moved into). + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's + * target's position (if available), otherwise (0, 0). + * @return {boolean} The returnValue of the event: false if preventDefault() was + * called on it, true otherwise. + */ +goog.testing.events.fireMouseOutEvent = function( + target, relatedTarget, opt_coords) { + var mouseout = + new goog.testing.events.Event(goog.events.EventType.MOUSEOUT, target); + mouseout.relatedTarget = relatedTarget; + goog.testing.events.setEventClientXY_(mouseout, opt_coords); + return goog.testing.events.fireBrowserEvent(mouseout); +}; + + +/** + * Simulates a mousedown event on the given target. + * @param {EventTarget} target The target for the event. + * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button; + * defaults to `goog.events.BrowserEvent.MouseButton.LEFT`. + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's + * target's position (if available), otherwise (0, 0). + * @param {Object=} opt_eventProperties Event properties to be mixed into the + * BrowserEvent. + * @return {boolean} The returnValue of the event: false if preventDefault() was + * called on it, true otherwise. + */ +goog.testing.events.fireMouseDownEvent = function( + target, opt_button, opt_coords, opt_eventProperties) { + var button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT; + button = !goog.events.BrowserFeature.HAS_W3C_BUTTON ? + goog.events.BrowserEvent.IE_BUTTON_MAP[button] : + button; + return goog.testing.events.fireMouseButtonEvent_( + goog.events.EventType.MOUSEDOWN, target, button, opt_coords, + opt_eventProperties); +}; + + +/** + * Simulates a mouseup event on the given target. + * @param {EventTarget} target The target for the event. + * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button; + * defaults to `goog.events.BrowserEvent.MouseButton.LEFT`. + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's + * target's position (if available), otherwise (0, 0). + * @param {Object=} opt_eventProperties Event properties to be mixed into the + * BrowserEvent. + * @return {boolean} The returnValue of the event: false if preventDefault() was + * called on it, true otherwise. + */ +goog.testing.events.fireMouseUpEvent = function( + target, opt_button, opt_coords, opt_eventProperties) { + var button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT; + button = !goog.events.BrowserFeature.HAS_W3C_BUTTON ? + goog.events.BrowserEvent.IE_BUTTON_MAP[button] : + button; + return goog.testing.events.fireMouseButtonEvent_( + goog.events.EventType.MOUSEUP, target, button, opt_coords, + opt_eventProperties); +}; + + +/** + * Simulates a click event on the given target. IE only supports click with + * the left mouse button. + * @param {EventTarget} target The target for the event. + * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button; + * defaults to `goog.events.BrowserEvent.MouseButton.LEFT`. + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's + * target's position (if available), otherwise (0, 0). + * @param {Object=} opt_eventProperties Event properties to be mixed into the + * BrowserEvent. + * @return {boolean} The returnValue of the event: false if preventDefault() was + * called on it, true otherwise. + */ +goog.testing.events.fireClickEvent = function( + target, opt_button, opt_coords, opt_eventProperties) { + return goog.testing.events.fireMouseButtonEvent_( + goog.events.EventType.CLICK, target, opt_button, opt_coords, + opt_eventProperties); +}; + + +/** + * Simulates a double-click event on the given target. Always double-clicks + * with the left mouse button since no browser supports double-clicking with + * any other buttons. + * @param {EventTarget} target The target for the event. + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's + * target's position (if available), otherwise (0, 0). + * @param {Object=} opt_eventProperties Event properties to be mixed into the + * BrowserEvent. + * @return {boolean} The returnValue of the event: false if preventDefault() was + * called on it, true otherwise. + */ +goog.testing.events.fireDoubleClickEvent = function( + target, opt_coords, opt_eventProperties) { + return goog.testing.events.fireMouseButtonEvent_( + goog.events.EventType.DBLCLICK, target, + goog.events.BrowserEvent.MouseButton.LEFT, opt_coords, + opt_eventProperties); +}; + + +/** + * Helper function to fire a mouse event. + * with the left mouse button since no browser supports double-clicking with + * any other buttons. + * @param {string} type The event type. + * @param {EventTarget} target The target for the event. + * @param {number=} opt_button Mouse button; defaults to + * `goog.events.BrowserEvent.MouseButton.LEFT`. + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's + * target's position (if available), otherwise (0, 0). + * @param {Object=} opt_eventProperties Event properties to be mixed into the + * BrowserEvent. + * @return {boolean} The returnValue of the event: false if preventDefault() was + * called on it, true otherwise. + * @private + */ +goog.testing.events.fireMouseButtonEvent_ = function( + type, target, opt_button, opt_coords, opt_eventProperties) { + var e = new goog.testing.events.Event(type, target); + e.button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT; + goog.testing.events.setEventClientXY_(e, opt_coords); + if (opt_eventProperties) { + goog.object.extend(e, opt_eventProperties); + } + return goog.testing.events.fireBrowserEvent(e); +}; + + +/** + * Simulates a contextmenu event on the given target. + * @param {EventTarget} target The target for the event. + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's + * target's position (if available), otherwise (0, 0). + * @return {boolean} The returnValue of the event: false if preventDefault() was + * called on it, true otherwise. + */ +goog.testing.events.fireContextMenuEvent = function(target, opt_coords) { + var button = (goog.userAgent.MAC && goog.userAgent.WEBKIT) ? + goog.events.BrowserEvent.MouseButton.LEFT : + goog.events.BrowserEvent.MouseButton.RIGHT; + var contextmenu = + new goog.testing.events.Event(goog.events.EventType.CONTEXTMENU, target); + contextmenu.button = !goog.events.BrowserFeature.HAS_W3C_BUTTON ? + goog.events.BrowserEvent.IE_BUTTON_MAP[button] : + button; + contextmenu.ctrlKey = goog.userAgent.MAC; + goog.testing.events.setEventClientXY_(contextmenu, opt_coords); + return goog.testing.events.fireBrowserEvent(contextmenu); +}; + + +/** + * Simulates a mousedown, contextmenu, and the mouseup on the given event + * target, with the right mouse button. + * @param {EventTarget} target The target for the event. + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's + * target's position (if available), otherwise (0, 0). + * @return {boolean} The returnValue of the sequence: false if preventDefault() + * was called on any of the events, true otherwise. + */ +goog.testing.events.fireContextMenuSequence = function(target, opt_coords) { + var props = goog.userAgent.MAC ? {ctrlKey: true} : {}; + var button = (goog.userAgent.MAC && goog.userAgent.WEBKIT) ? + goog.events.BrowserEvent.MouseButton.LEFT : + goog.events.BrowserEvent.MouseButton.RIGHT; + + var result = + goog.testing.events.fireMouseDownEvent(target, button, opt_coords, props); + if (goog.userAgent.WINDOWS) { + // All browsers are consistent on Windows. + result = goog.testing.events.eagerAnd_( + result, + goog.testing.events.fireMouseUpEvent(target, button, opt_coords), + goog.testing.events.fireContextMenuEvent(target, opt_coords)); + } else { + result = goog.testing.events.eagerAnd_( + result, goog.testing.events.fireContextMenuEvent(target, opt_coords)); + + // GECKO on Mac and Linux always fires the mouseup after the contextmenu. + + // WEBKIT is really weird. + // + // On Linux, it sometimes fires mouseup, but most of the time doesn't. + // It's really hard to reproduce consistently. I think there's some + // internal race condition. If contextmenu is preventDefaulted, then + // mouseup always fires. + // + // On Mac, it always fires mouseup and then fires a click. + result = goog.testing.events.eagerAnd_( + result, + goog.testing.events.fireMouseUpEvent( + target, button, opt_coords, props)); + + if (goog.userAgent.WEBKIT && goog.userAgent.MAC) { + result = goog.testing.events.eagerAnd_( + result, + goog.testing.events.fireClickEvent( + target, button, opt_coords, props)); + } + } + return result; +}; + + +/** + * Simulates a popstate event on the given target. + * @param {EventTarget} target The target for the event. + * @param {Object} state History state object. + * @return {boolean} The returnValue of the event: false if preventDefault() was + * called on it, true otherwise. + */ +goog.testing.events.firePopStateEvent = function(target, state) { + var e = /** @type {!PopStateEvent} */ (/** @type {!Event} */ ( + new goog.testing.events.Event(goog.events.EventType.POPSTATE, target))); + e.state = state; + return goog.testing.events.fireBrowserEvent(e); +}; + + +/** + * Simulate a blur event on the given target. + * @param {EventTarget} target The target for the event. + * @return {boolean} The value returned by firing the blur browser event, + * which returns false iff 'preventDefault' was invoked. + */ +goog.testing.events.fireBlurEvent = function(target) { + var e = new goog.testing.events.Event(goog.events.EventType.BLUR, target); + return goog.testing.events.fireBrowserEvent(e); +}; + + +/** + * Simulate a focus event on the given target. + * @param {EventTarget} target The target for the event. + * @return {boolean} The value returned by firing the focus browser event, + * which returns false iff 'preventDefault' was invoked. + */ +goog.testing.events.fireFocusEvent = function(target) { + var e = new goog.testing.events.Event(goog.events.EventType.FOCUS, target); + return goog.testing.events.fireBrowserEvent(e); +}; + + +/** + * Simulate a focus-in event on the given target. + * @param {!EventTarget} target The target for the event. + * @return {boolean} The value returned by firing the focus-in browser event, + * which returns false iff 'preventDefault' was invoked. + */ +goog.testing.events.fireFocusInEvent = function(target) { + var e = new goog.testing.events.Event(goog.events.EventType.FOCUSIN, target); + return goog.testing.events.fireBrowserEvent(e); +}; + + +/** + * Simulates an event's capturing and bubbling phases. + * @param {Event} event A simulated native event. It will be wrapped in a + * normalized BrowserEvent and dispatched to Closure listeners on all + * ancestors of its target (inclusive). + * @return {boolean} The returnValue of the event: false if preventDefault() was + * called on it, true otherwise. + */ +goog.testing.events.fireBrowserEvent = function(event) { + event = /** @type {!goog.testing.events.Event} */ (event); + + event.returnValue_ = true; + + // generate a list of ancestors + var ancestors = []; + for (var current = event.target; current; current = current.parentNode) { + ancestors.push(current); + } + + // dispatch capturing listeners + for (var j = ancestors.length - 1; j >= 0 && !event.propagationStopped_; + j--) { + goog.events.fireListeners( + ancestors[j], event.type, true, + new goog.events.BrowserEvent(event, ancestors[j])); + } + + // dispatch bubbling listeners + for (var j = 0; j < ancestors.length && !event.propagationStopped_; j++) { + goog.events.fireListeners( + ancestors[j], event.type, false, + new goog.events.BrowserEvent(event, ancestors[j])); + } + + return event.returnValue_; +}; + + +/** + * Simulates a touchstart event on the given target. + * @param {EventTarget} target The target for the event. + * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's + * target's position (if available), otherwise (0, 0). + * @param {Object=} opt_eventProperties Event properties to be mixed into the + * BrowserEvent. + * @return {boolean} The returnValue of the event: false if preventDefault() was + * called on it, true otherwise. + */ +goog.testing.events.fireTouchStartEvent = function( + target, opt_coords, opt_eventProperties) { + // TODO: Support multi-touch events with array of coordinates. + var touchstart = + new goog.testing.events.Event(goog.events.EventType.TOUCHSTART, target); + goog.testing.events.setEventClientXY_(touchstart, opt_coords); + if (opt_eventProperties) { + goog.object.extend(touchstart, opt_eventProperties); + } + return goog.testing.events.fireBrowserEvent(touchstart); +}; + + +/** + * Simulates a touchmove event on the given target. + * @param {EventTarget} target The target for the event. + * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's + * target's position (if available), otherwise (0, 0). + * @param {Object=} opt_eventProperties Event properties to be mixed into the + * BrowserEvent. + * @return {boolean} The returnValue of the event: false if preventDefault() was + * called on it, true otherwise. + */ +goog.testing.events.fireTouchMoveEvent = function( + target, opt_coords, opt_eventProperties) { + // TODO: Support multi-touch events with array of coordinates. + var touchmove = + new goog.testing.events.Event(goog.events.EventType.TOUCHMOVE, target); + goog.testing.events.setEventClientXY_(touchmove, opt_coords); + if (opt_eventProperties) { + goog.object.extend(touchmove, opt_eventProperties); + } + return goog.testing.events.fireBrowserEvent(touchmove); +}; + + +/** + * Simulates a touchend event on the given target. + * @param {EventTarget} target The target for the event. + * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's + * target's position (if available), otherwise (0, 0). + * @param {Object=} opt_eventProperties Event properties to be mixed into the + * BrowserEvent. + * @return {boolean} The returnValue of the event: false if preventDefault() was + * called on it, true otherwise. + */ +goog.testing.events.fireTouchEndEvent = function( + target, opt_coords, opt_eventProperties) { + // TODO: Support multi-touch events with array of coordinates. + var touchend = + new goog.testing.events.Event(goog.events.EventType.TOUCHEND, target); + goog.testing.events.setEventClientXY_(touchend, opt_coords); + if (opt_eventProperties) { + goog.object.extend(touchend, opt_eventProperties); + } + return goog.testing.events.fireBrowserEvent(touchend); +}; + + +/** + * Simulates a simple touch sequence on the given target. + * @param {EventTarget} target The target for the event. + * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event + * target's position (if available), otherwise (0, 0). + * @param {Object=} opt_eventProperties Event properties to be mixed into the + * BrowserEvent. + * @return {boolean} The returnValue of the sequence: false if preventDefault() + * was called on any of the events, true otherwise. + */ +goog.testing.events.fireTouchSequence = function( + target, opt_coords, opt_eventProperties) { + // TODO: Support multi-touch events with array of coordinates. + // Fire touchstart, touchmove, touchend then return the AND of the 2. + return goog.testing.events.eagerAnd_( + goog.testing.events.fireTouchStartEvent( + target, opt_coords, opt_eventProperties), + goog.testing.events.fireTouchEndEvent( + target, opt_coords, opt_eventProperties)); +}; + + +/** + * Mixins a listenable into the given object. This turns the object + * into a goog.events.Listenable. This is useful, for example, when + * you need to mock a implementation of listenable and still want it + * to work with goog.events. + * @param {!Object} obj The object to mixin into. + */ +goog.testing.events.mixinListenable = function(obj) { + var listenable = new goog.events.EventTarget(); + + listenable.setTargetForTesting(obj); + + var listenablePrototype = goog.events.EventTarget.prototype; + var disposablePrototype = goog.Disposable.prototype; + for (var key in listenablePrototype) { + if (listenablePrototype.hasOwnProperty(key) || + disposablePrototype.hasOwnProperty(key)) { + var member = listenablePrototype[key]; + if (goog.isFunction(member)) { + obj[key] = goog.bind(member, listenable); + } else { + obj[key] = member; + } + } + } +}; + +/** + * Returns the boolean AND of all parameters. + * + * Unlike directly using `&&`, using this function cannot employ + * short-circuiting; all side effects of resolving parameters will occur before + * entering the function body. + * + * @param {boolean} first + * @param {...boolean} rest + * @return {boolean} + * @private + */ +goog.testing.events.eagerAnd_ = function(first, rest) { + for (var i = 1; i < arguments.length; i++) { + first = first && arguments[i]; + } + return first; +}; diff --git a/closure-library/closure/goog/testing/events/matchers.js b/closure-library/closure/goog/testing/events/matchers.js new file mode 100644 index 0000000000..f5f3fd21ef --- /dev/null +++ b/closure-library/closure/goog/testing/events/matchers.js @@ -0,0 +1,42 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Mock matchers for event related arguments. + */ + +goog.setTestOnly('goog.testing.events.EventMatcher'); +goog.provide('goog.testing.events.EventMatcher'); + +goog.require('goog.events.Event'); +goog.require('goog.testing.mockmatchers.ArgumentMatcher'); + + + +/** + * A matcher that verifies that an argument is a `goog.events.Event` of a + * particular type. + * @param {string} type The single type the event argument must be of. + * @constructor + * @extends {goog.testing.mockmatchers.ArgumentMatcher} + * @final + */ +goog.testing.events.EventMatcher = function(type) { + goog.testing.mockmatchers.ArgumentMatcher.call(this, function(obj) { + return obj instanceof goog.events.Event && obj.type == type; + }, 'isEventOfType(' + type + ')'); +}; +goog.inherits( + goog.testing.events.EventMatcher, + goog.testing.mockmatchers.ArgumentMatcher); diff --git a/closure-library/closure/goog/testing/events/onlinehandler.js b/closure-library/closure/goog/testing/events/onlinehandler.js new file mode 100644 index 0000000000..35e58fc1d5 --- /dev/null +++ b/closure-library/closure/goog/testing/events/onlinehandler.js @@ -0,0 +1,66 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview NetworkStatusMonitor test double. + * @author dbk@google.com (David Barrett-Kahn) + */ + +goog.setTestOnly('goog.testing.events.OnlineHandler'); +goog.provide('goog.testing.events.OnlineHandler'); + +goog.require('goog.events.EventTarget'); +goog.require('goog.net.NetworkStatusMonitor'); + + + +/** + * NetworkStatusMonitor test double. + * @param {boolean} initialState The initial online state of the mock. + * @constructor + * @extends {goog.events.EventTarget} + * @implements {goog.net.NetworkStatusMonitor} + * @final + */ +goog.testing.events.OnlineHandler = function(initialState) { + goog.testing.events.OnlineHandler.base(this, 'constructor'); + + /** + * Whether the mock is online. + * @private {boolean} + */ + this.online_ = initialState; +}; +goog.inherits(goog.testing.events.OnlineHandler, goog.events.EventTarget); + + +/** @override */ +goog.testing.events.OnlineHandler.prototype.isOnline = function() { + return this.online_; +}; + + +/** + * Sets the online state. + * @param {boolean} newOnlineState The new online state. + */ +goog.testing.events.OnlineHandler.prototype.setOnline = function( + newOnlineState) { + if (newOnlineState != this.online_) { + this.online_ = newOnlineState; + this.dispatchEvent( + newOnlineState ? goog.net.NetworkStatusMonitor.EventType.ONLINE : + goog.net.NetworkStatusMonitor.EventType.OFFLINE); + } +}; diff --git a/closure-library/closure/goog/testing/expectedfailures.js b/closure-library/closure/goog/testing/expectedfailures.js new file mode 100644 index 0000000000..3cc2a4d8a6 --- /dev/null +++ b/closure-library/closure/goog/testing/expectedfailures.js @@ -0,0 +1,249 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Helper class to allow for expected unit test failures. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.setTestOnly('goog.testing.ExpectedFailures'); +goog.provide('goog.testing.ExpectedFailures'); + +goog.require('goog.asserts'); +goog.require('goog.debug.DivConsole'); +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.events'); +goog.require('goog.events.EventType'); +goog.require('goog.log'); +goog.require('goog.style'); +goog.require('goog.testing.JsUnitException'); +goog.require('goog.testing.TestCase'); +goog.require('goog.testing.asserts'); + + + +/** + * Helper class for allowing some unit tests to fail, particularly designed to + * mark tests that should be fixed on a given browser. + * + *
+ * var expectedFailures = new goog.testing.ExpectedFailures();
+ *
+ * function tearDown() {
+ *   expectedFailures.handleTearDown();
+ * }
+ *
+ * function testSomethingThatBreaksInWebKit() {
+ *   expectedFailures.expectFailureFor(goog.userAgent.WEBKIT);
+ *
+ *   try {
+ *     ...
+ *     assert(somethingThatFailsInWebKit);
+ *     ...
+ *   } catch (e) {
+ *     expectedFailures.handleException(e);
+ *   }
+ * }
+ * 
+ * + * @constructor + * @final + */ +goog.testing.ExpectedFailures = function() { + goog.testing.ExpectedFailures.setUpConsole_(); + this.reset_(); +}; + + +/** + * The lazily created debugging console. + * @type {goog.debug.DivConsole?} + * @private + */ +goog.testing.ExpectedFailures.console_ = null; + + +/** + * Logger for the expected failures. + * @type {goog.log.Logger} + * @private + */ +goog.testing.ExpectedFailures.prototype.logger_ = + goog.log.getLogger('goog.testing.ExpectedFailures'); + + +/** + * Whether or not we are expecting failure. + * @type {boolean} + * @private + */ +goog.testing.ExpectedFailures.prototype.expectingFailure_; + + +/** + * The string to emit upon an expected failure. + * @type {string} + * @private + */ +goog.testing.ExpectedFailures.prototype.failureMessage_; + + +/** + * An array of suppressed failures. + * @type {Array} + * @private + */ +goog.testing.ExpectedFailures.prototype.suppressedFailures_; + + +/** + * Sets up the debug console, if it isn't already set up. + * @private + */ +goog.testing.ExpectedFailures.setUpConsole_ = function() { + if (!goog.testing.ExpectedFailures.console_) { + var xButton = goog.dom.createDom( + goog.dom.TagName.DIV, { + 'style': 'position: absolute; border-left:1px solid #333;' + + 'border-bottom:1px solid #333; right: 0; top: 0; width: 1em;' + + 'height: 1em; cursor: pointer; background-color: #cde;' + + 'text-align: center; color: black' + }, + 'X'); + var div = goog.dom.createDom( + goog.dom.TagName.DIV, { + 'style': 'position: absolute; border: 1px solid #333; right: 10px;' + + 'top : 10px; width: 400px; display: none' + }, + xButton); + document.body.appendChild(div); + goog.events.listen(xButton, goog.events.EventType.CLICK, function() { + goog.style.setElementShown(div, false); + }); + + goog.testing.ExpectedFailures.console_ = new goog.debug.DivConsole(div); + goog.log.addHandler( + goog.testing.ExpectedFailures.prototype.logger_, + goog.bind(goog.style.setElementShown, null, div, true)); + goog.log.addHandler( + goog.testing.ExpectedFailures.prototype.logger_, + goog.bind( + goog.testing.ExpectedFailures.console_.addLogRecord, + goog.testing.ExpectedFailures.console_)); + } +}; + + +/** + * Register to expect failure for the given condition. Multiple calls to this + * function act as a boolean OR. The first applicable message will be used. + * @param {boolean} condition Whether to expect failure. + * @param {string=} opt_message Descriptive message of this expected failure. + */ +goog.testing.ExpectedFailures.prototype.expectFailureFor = function( + condition, opt_message) { + this.expectingFailure_ = this.expectingFailure_ || condition; + if (condition) { + this.failureMessage_ = this.failureMessage_ || opt_message || ''; + } +}; + + +/** + * Determines if the given exception was expected. + * @param {Object} ex The exception to check. + * @return {boolean} Whether the exception was expected. + */ +goog.testing.ExpectedFailures.prototype.isExceptionExpected = function(ex) { + return this.expectingFailure_ && ex instanceof goog.testing.JsUnitException; +}; + + +/** + * Handle an exception, suppressing it if it is a unit test failure that we + * expected. + * @param {Error} ex The exception to handle. + */ +goog.testing.ExpectedFailures.prototype.handleException = function(ex) { + if (this.isExceptionExpected(ex)) { + goog.asserts.assertInstanceof(ex, goog.testing.JsUnitException); + goog.log.info( + this.logger_, 'Suppressing test failure in ' + + goog.testing.TestCase.currentTestName + ':' + + (this.failureMessage_ ? '\n(' + this.failureMessage_ + ')' : ''), + ex); + this.suppressedFailures_.push(ex); + goog.testing.TestCase.invalidateAssertionException(ex); + return; + } + + // Rethrow the exception if we weren't expecting it or if it is a normal + // exception. + throw ex; +}; + + +/** + * Run the given function, catching any expected failures. + * @param {Function} func The function to run. + * @param {boolean=} opt_lenient Whether to ignore if the expected failures + * didn't occur. In this case a warning will be logged in handleTearDown. + */ +goog.testing.ExpectedFailures.prototype.run = function(func, opt_lenient) { + try { + func(); + } catch (ex) { + this.handleException(ex); + } + + if (!opt_lenient && this.expectingFailure_ && + !this.suppressedFailures_.length) { + fail(this.getExpectationMessage_()); + } +}; + + +/** + * @return {string} A warning describing an expected failure that didn't occur. + * @private + */ +goog.testing.ExpectedFailures.prototype.getExpectationMessage_ = function() { + return 'Expected a test failure in \'' + + goog.testing.TestCase.currentTestName + '\' but the test passed.'; +}; + + +/** + * Handle the tearDown phase of a test, alerting the user if an expected test + * was not suppressed. + */ +goog.testing.ExpectedFailures.prototype.handleTearDown = function() { + if (this.expectingFailure_ && !this.suppressedFailures_.length) { + goog.log.warning(this.logger_, this.getExpectationMessage_()); + } + this.reset_(); +}; + + +/** + * Reset internal state. + * @private + */ +goog.testing.ExpectedFailures.prototype.reset_ = function() { + this.expectingFailure_ = false; + this.failureMessage_ = ''; + this.suppressedFailures_ = []; +}; diff --git a/closure-library/closure/goog/testing/fs/blob.js b/closure-library/closure/goog/testing/fs/blob.js new file mode 100644 index 0000000000..bc7a58caef --- /dev/null +++ b/closure-library/closure/goog/testing/fs/blob.js @@ -0,0 +1,191 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// 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 Mock blob object. + * + */ + +goog.setTestOnly('goog.testing.fs.Blob'); +goog.provide('goog.testing.fs.Blob'); + +goog.require('goog.crypt'); +goog.require('goog.crypt.base64'); + + + +/** + * A mock Blob object. The data is stored as an Array of bytes, a "byte" being a + * JS number in the range 0-255. + * + * This blob simplifies writing test code because it has the toString() method + * that returns immediately, while the File API only provides asynchronous + * reads. + * @see https://www.w3.org/TR/FileAPI/#constructorBlob + * + * @param {(string|Array<(string|number|!Uint8Array)>)=} opt_data The data + * encapsulated by the blob. + * @param {string=} opt_type The mime type of the blob. + * @constructor + */ +goog.testing.fs.Blob = function(opt_data, opt_type) { + /** + * @see http://www.w3.org/TR/FileAPI/#dfn-type + * @type {string} + */ + this.type = opt_type || ''; + + /** + * The data encapsulated by the blob as an Array of bytes, a "byte" being a + * JS number in the range 0-255. + * @private {!Array} + */ + this.data_ = []; + + /** + * @see http://www.w3.org/TR/FileAPI/#dfn-size + * @type {number} + */ + this.size = 0; + + this.setDataInternal(opt_data || ''); +}; + + +/** + * Creates a blob with bytes of a blob ranging from the optional start + * parameter up to but not including the optional end parameter, and with a type + * attribute that is the value of the optional contentType parameter. + * @see http://www.w3.org/TR/FileAPI/#dfn-slice + * @param {number=} opt_start The start byte offset. + * @param {number=} opt_end The end point of a slice. + * @param {string=} opt_contentType The type of the resulting Blob. + * @return {!goog.testing.fs.Blob} The result blob of the slice operation. + */ +goog.testing.fs.Blob.prototype.slice = function( + opt_start, opt_end, opt_contentType) { + var relativeStart; + if (goog.isNumber(opt_start)) { + relativeStart = (opt_start < 0) ? Math.max(this.size + opt_start, 0) : + Math.min(opt_start, this.size); + } else { + relativeStart = 0; + } + var relativeEnd; + if (goog.isNumber(opt_end)) { + relativeEnd = (opt_end < 0) ? Math.max(this.size + opt_end, 0) : + Math.min(opt_end, this.size); + } else { + relativeEnd = this.size; + } + var span = Math.max(relativeEnd - relativeStart, 0); + var blob = new goog.testing.fs.Blob( + this.data_.slice(relativeStart, relativeStart + span), opt_contentType); + return blob; +}; + + +/** + * @return {string} The data encapsulated by the blob as an UTF-8 string. + * @override + */ +goog.testing.fs.Blob.prototype.toString = function() { + return goog.crypt.utf8ByteArrayToString(this.data_); +}; + + +/** + * @return {!ArrayBuffer} The data encapsulated by the blob as an + * ArrayBuffer. + */ +goog.testing.fs.Blob.prototype.toArrayBuffer = function() { + var buf = new ArrayBuffer(this.data_.length); + var arr = new Uint8Array(buf); + for (var i = 0; i < this.data_.length; i++) { + arr[i] = this.data_[i]; + } + return buf; +}; + + +/** + * @return {string} The string data encapsulated by the blob as a data: URI. + */ +goog.testing.fs.Blob.prototype.toDataUrl = function() { + return 'data:' + this.type + ';base64,' + + goog.crypt.base64.encodeByteArray(this.data_); +}; + + +/** + * Sets the internal contents of the blob to an Array of bytes. This should + * only be called by other functions inside the `goog.testing.fs` + * namespace. + * @param {string|Array} data The data to write + * into the blob. + * @package + */ +goog.testing.fs.Blob.prototype.setDataInternal = function(data) { + this.data_ = []; + if (typeof data === 'string') { + this.appendString_(data); + } else if (data instanceof Array) { + for (var i = 0; i < data.length; i++) { + var value = data[i]; + if (typeof value === 'string') { + this.appendString_(value); + } else if (typeof value === 'number') { // Assume Bytes array. + this.appendByte_(value); + } else if (value instanceof Uint8Array) { + this.appendUint8_(value); + } + } + } + this.size = this.data_.length; +}; + + +/** + * Converts the data from string to Array of bytes and appends to the blob + * content. + * @param {string} data The string to append to the blob content. + * @private + */ +goog.testing.fs.Blob.prototype.appendString_ = function(data) { + Array.prototype.push.apply( + this.data_, goog.crypt.stringToUtf8ByteArray(data)); +}; + + +/** + * Appends a byte (as a number between 0 to 255) to the blob content. + * @param {number} data The byte to append. + * @private + */ +goog.testing.fs.Blob.prototype.appendByte_ = function(data) { + this.data_.push(data); +}; + + +/** + * Converts the data from Uint8Array to Array of bytes and appends it to the + * blob content. + * @param {!Uint8Array} data The array to append to the blob content. + * @private + */ +goog.testing.fs.Blob.prototype.appendUint8_ = function(data) { + for (var i = 0; i < data.length; i++) { + this.data_.push(data[i]); + } +}; diff --git a/closure-library/closure/goog/testing/fs/entry.js b/closure-library/closure/goog/testing/fs/entry.js new file mode 100644 index 0000000000..080eaa07c6 --- /dev/null +++ b/closure-library/closure/goog/testing/fs/entry.js @@ -0,0 +1,644 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// 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 Mock filesystem objects. These are all in the same file to + * avoid circular dependency issues. + * + */ + +goog.setTestOnly('goog.testing.fs.DirectoryEntry'); +goog.provide('goog.testing.fs.DirectoryEntry'); +goog.provide('goog.testing.fs.Entry'); +goog.provide('goog.testing.fs.FileEntry'); + +goog.require('goog.Timer'); +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.async.Deferred'); +goog.require('goog.fs.DirectoryEntry'); +goog.require('goog.fs.DirectoryEntryImpl'); +goog.require('goog.fs.Entry'); +goog.require('goog.fs.Error'); +goog.require('goog.fs.FileEntry'); +goog.require('goog.functions'); +goog.require('goog.object'); +goog.require('goog.string'); +goog.require('goog.testing.fs.File'); +goog.require('goog.testing.fs.FileWriter'); + +goog.forwardDeclare('goog.testing.fs.FileSystem'); + + + +/** + * A mock filesystem entry object. + * + * @param {!goog.testing.fs.FileSystem} fs The filesystem containing this entry. + * @param {!goog.testing.fs.DirectoryEntry} parent The directory entry directly + * containing this entry. + * @param {string} name The name of this entry. + * @constructor + * @implements {goog.fs.Entry} + */ +goog.testing.fs.Entry = function(fs, parent, name) { + /** + * This entry's filesystem. + * @type {!goog.testing.fs.FileSystem} + * @private + */ + this.fs_ = fs; + + /** + * The name of this entry. + * @type {string} + * @private + */ + this.name_ = name; + + /** + * The parent of this entry. + * @type {!goog.testing.fs.DirectoryEntry} + */ + this.parent = parent; +}; + + +/** + * Whether or not this entry has been deleted. + * @type {boolean} + */ +goog.testing.fs.Entry.prototype.deleted = false; + + +/** @override */ +goog.testing.fs.Entry.prototype.isFile = goog.abstractMethod; + + +/** @override */ +goog.testing.fs.Entry.prototype.isDirectory = goog.abstractMethod; + + +/** @override */ +goog.testing.fs.Entry.prototype.getName = function() { + return this.name_; +}; + + +/** @override */ +goog.testing.fs.Entry.prototype.getFullPath = function() { + if (this.getName() == '' || this.parent.getName() == '') { + // The root directory has an empty name + return '/' + this.name_; + } else { + return this.parent.getFullPath() + '/' + this.name_; + } +}; + + +/** + * @return {!goog.testing.fs.FileSystem} + * @override + */ +goog.testing.fs.Entry.prototype.getFileSystem = function() { + return this.fs_; +}; + + +/** @override */ +goog.testing.fs.Entry.prototype.getLastModified = goog.abstractMethod; + + +/** @override */ +goog.testing.fs.Entry.prototype.getMetadata = goog.abstractMethod; + + +/** @override */ +goog.testing.fs.Entry.prototype.moveTo = function(parent, opt_newName) { + var msg = 'moving ' + this.getFullPath() + ' into ' + parent.getFullPath() + + (opt_newName ? ', renaming to ' + opt_newName : ''); + var newFile; + return this.checkNotDeleted(msg) + .addCallback(function() { return this.copyTo(parent, opt_newName); }) + .addCallback(function(file) { + newFile = file; + return this.remove(); + }) + .addCallback(function() { return newFile; }); +}; + + +/** @override */ +goog.testing.fs.Entry.prototype.copyTo = function(parent, opt_newName) { + goog.asserts.assert(parent instanceof goog.testing.fs.DirectoryEntry); + var msg = 'copying ' + this.getFullPath() + ' into ' + parent.getFullPath() + + (opt_newName ? ', renaming to ' + opt_newName : ''); + var self = this; + return this.checkNotDeleted(msg).addCallback(function() { + goog.asserts.assert(parent instanceof goog.testing.fs.DirectoryEntry); + var name = opt_newName || self.getName(); + var entry = self.clone(); + /** @type {!goog.testing.fs.DirectoryEntry} */ (parent).children[name] = + entry; + parent.lastModifiedTimestamp_ = goog.now(); + entry.name_ = name; + entry.parent = /** @type {!goog.testing.fs.DirectoryEntry} */ (parent); + return entry; + }); +}; + + +/** + * @return {!goog.testing.fs.Entry} A shallow copy of this entry object. + */ +goog.testing.fs.Entry.prototype.clone = goog.abstractMethod; + + +/** @override */ +goog.testing.fs.Entry.prototype.toUrl = function(opt_mimetype) { + return 'fakefilesystem:' + this.getFullPath(); +}; + + +/** @override */ +goog.testing.fs.Entry.prototype.toUri = goog.testing.fs.Entry.prototype.toUrl; + + +/** @override */ +goog.testing.fs.Entry.prototype.wrapEntry = goog.abstractMethod; + + +/** @override */ +goog.testing.fs.Entry.prototype.remove = function() { + var msg = 'removing ' + this.getFullPath(); + var self = this; + return this.checkNotDeleted(msg).addCallback(function() { + delete this.parent.children[self.getName()]; + self.parent.lastModifiedTimestamp_ = goog.now(); + self.deleted = true; + return; + }); +}; + + +/** @override */ +goog.testing.fs.Entry.prototype.getParent = function() { + var msg = 'getting parent of ' + this.getFullPath(); + return this.checkNotDeleted(msg).addCallback(function() { + return this.parent; + }); +}; + + +/** + * Return a deferred that will call its errback if this entry has been deleted. + * In addition, the deferred will only run after a timeout of 0, and all its + * callbacks will run with the entry as "this". + * + * @param {string} action The name of the action being performed. For error + * reporting. + * @return {!goog.async.Deferred} The deferred that will be called after a + * timeout of 0. + * @protected + */ +goog.testing.fs.Entry.prototype.checkNotDeleted = function(action) { + var d = new goog.async.Deferred(undefined, this); + goog.Timer.callOnce(function() { + if (this.deleted) { + var err = new goog.fs.Error({'name': 'NotFoundError'}, action); + d.errback(err); + } else { + d.callback(); + } + }, 0, this); + return d; +}; + + + +/** + * A mock directory entry object. + * + * @param {!goog.testing.fs.FileSystem} fs The filesystem containing this entry. + * @param {goog.testing.fs.DirectoryEntry} parent The directory entry directly + * containing this entry. If this is null, that means this is the root + * directory and so is its own parent. + * @param {string} name The name of this entry. + * @param {!Object} children The map of child names to + * entry objects. + * @constructor + * @extends {goog.testing.fs.Entry} + * @implements {goog.fs.DirectoryEntry} + * @final + */ +goog.testing.fs.DirectoryEntry = function(fs, parent, name, children) { + goog.testing.fs.DirectoryEntry.base( + this, 'constructor', fs, parent || this, name); + + /** + * The map of child names to entry objects. + * @type {!Object} + */ + this.children = children; + + /** + * The modification time of the directory. Measured using goog.now, which may + * be overridden with mock time providers. + * @type {number} + * @private + */ + this.lastModifiedTimestamp_ = goog.now(); +}; +goog.inherits(goog.testing.fs.DirectoryEntry, goog.testing.fs.Entry); + + +/** + * Constructs and returns the metadata object for this entry. + * @return {{modificationTime: Date}} The metadata object. + * @private + */ +goog.testing.fs.DirectoryEntry.prototype.getMetadata_ = function() { + return {'modificationTime': new Date(this.lastModifiedTimestamp_)}; +}; + + +/** @override */ +goog.testing.fs.DirectoryEntry.prototype.isFile = function() { + return false; +}; + + +/** @override */ +goog.testing.fs.DirectoryEntry.prototype.isDirectory = function() { + return true; +}; + + +/** @override */ +goog.testing.fs.DirectoryEntry.prototype.getLastModified = function() { + var msg = 'reading last modified date for ' + this.getFullPath(); + return this.checkNotDeleted(msg).addCallback(function() { + return new Date(this.lastModifiedTimestamp_); + }); +}; + + +/** @override */ +goog.testing.fs.DirectoryEntry.prototype.getMetadata = function() { + var msg = 'reading metadata for ' + this.getFullPath(); + return this.checkNotDeleted(msg).addCallback(function() { + return this.getMetadata_(); + }); +}; + + +/** @override */ +goog.testing.fs.DirectoryEntry.prototype.clone = function() { + return new goog.testing.fs.DirectoryEntry( + this.getFileSystem(), this.parent, this.getName(), this.children); +}; + + +/** @override */ +goog.testing.fs.DirectoryEntry.prototype.remove = function() { + if (!goog.object.isEmpty(this.children)) { + var d = new goog.async.Deferred(); + goog.Timer.callOnce(function() { + d.errback(new goog.fs.Error( + {'name': 'InvalidModificationError'}, + 'removing ' + this.getFullPath())); + }, 0, this); + return d; + } else if (this != this.getFileSystem().getRoot()) { + return goog.testing.fs.DirectoryEntry.base(this, 'remove'); + } else { + // Root directory, do nothing. + return goog.async.Deferred.succeed(); + } +}; + + +/** @override */ +goog.testing.fs.DirectoryEntry.prototype.getFile = function( + path, opt_behavior) { + var msg = 'loading file ' + path + ' from ' + this.getFullPath(); + opt_behavior = opt_behavior || goog.fs.DirectoryEntry.Behavior.DEFAULT; + return this.checkNotDeleted(msg).addCallback(function() { + try { + return goog.async.Deferred.succeed(this.getFileSync(path, opt_behavior)); + } catch (e) { + return goog.async.Deferred.fail(e); + } + }); +}; + + +/** @override */ +goog.testing.fs.DirectoryEntry.prototype.getDirectory = function( + path, opt_behavior) { + var msg = 'loading directory ' + path + ' from ' + this.getFullPath(); + opt_behavior = opt_behavior || goog.fs.DirectoryEntry.Behavior.DEFAULT; + return this.checkNotDeleted(msg).addCallback(function() { + try { + return goog.async.Deferred.succeed( + this.getDirectorySync(path, opt_behavior)); + } catch (e) { + return goog.async.Deferred.fail(e); + } + }); +}; + + +/** + * Get a file entry synchronously, without waiting for a Deferred to resolve. + * + * @param {string} path The path to the file, relative to this directory. + * @param {goog.fs.DirectoryEntry.Behavior=} opt_behavior The behavior for + * loading the file. + * @param {string=} opt_data The string data encapsulated by the blob. + * @param {string=} opt_type The mime type of the blob. + * @return {!goog.testing.fs.FileEntry} The loaded file. + */ +goog.testing.fs.DirectoryEntry.prototype.getFileSync = function( + path, opt_behavior, opt_data, opt_type) { + opt_behavior = opt_behavior || goog.fs.DirectoryEntry.Behavior.DEFAULT; + return ( + /** @type {!goog.testing.fs.FileEntry} */ ( + this.getEntry_( + path, opt_behavior, true /* isFile */, + goog.bind(function(parent, name) { + return new goog.testing.fs.FileEntry( + this.getFileSystem(), parent, name, + goog.isDef(opt_data) ? opt_data : '', opt_type); + }, this)))); +}; + + +/** + * Creates a file synchronously. This is a shorthand for getFileSync, useful for + * setting up tests. + * + * @param {string} path The path to the file, relative to this directory. + * @return {!goog.testing.fs.FileEntry} The created file. + */ +goog.testing.fs.DirectoryEntry.prototype.createFileSync = function(path) { + return this.getFileSync(path, goog.fs.DirectoryEntry.Behavior.CREATE); +}; + + +/** + * Get a directory synchronously, without waiting for a Deferred to resolve. + * + * @param {string} path The path to the directory, relative to this one. + * @param {goog.fs.DirectoryEntry.Behavior=} opt_behavior The behavior for + * loading the directory. + * @return {!goog.testing.fs.DirectoryEntry} The loaded directory. + */ +goog.testing.fs.DirectoryEntry.prototype.getDirectorySync = function( + path, opt_behavior) { + opt_behavior = opt_behavior || goog.fs.DirectoryEntry.Behavior.DEFAULT; + return ( + /** @type {!goog.testing.fs.DirectoryEntry} */ ( + this.getEntry_( + path, opt_behavior, false /* isFile */, + goog.bind(function(parent, name) { + return new goog.testing.fs.DirectoryEntry( + this.getFileSystem(), parent, name, {}); + }, this)))); +}; + + +/** + * Creates a directory synchronously. This is a shorthand for getFileSync, + * useful for setting up tests. + * + * @param {string} path The path to the directory, relative to this directory. + * @return {!goog.testing.fs.DirectoryEntry} The created directory. + */ +goog.testing.fs.DirectoryEntry.prototype.createDirectorySync = function(path) { + return this.getDirectorySync(path, goog.fs.DirectoryEntry.Behavior.CREATE); +}; + + +/** + * Get a file or directory entry from a path. This handles parsing the path for + * subdirectories and throwing appropriate errors should something go wrong. + * + * @param {string} path The path to the entry, relative to this directory. + * @param {goog.fs.DirectoryEntry.Behavior} behavior The behavior for loading + * the entry. + * @param {boolean} isFile Whether a file or directory is being loaded. + * @param {function(!goog.testing.fs.DirectoryEntry, string) : + * !goog.testing.fs.Entry} createFn + * The function for creating the entry if it doesn't yet exist. This is + * passed the parent entry and the name of the new entry. + * @return {!goog.testing.fs.Entry} The loaded entry. + * @private + */ +goog.testing.fs.DirectoryEntry.prototype.getEntry_ = function( + path, behavior, isFile, createFn) { + // Filter out leading, trailing, and duplicate slashes. + var components = goog.array.filter(path.split('/'), goog.functions.identity); + + var basename = /** @type {string} */ (goog.array.peek(components)) || ''; + var dir = + goog.string.startsWith(path, '/') ? this.getFileSystem().getRoot() : this; + + goog.array.forEach(components.slice(0, -1), function(p) { + var subdir = dir.children[p]; + if (!subdir) { + throw new goog.fs.Error( + {'name': 'NotFoundError'}, + 'loading ' + path + ' from ' + this.getFullPath() + ' (directory ' + + dir.getFullPath() + '/' + p + ')'); + } + dir = subdir; + }, this); + + // If there is no basename, the path must resolve to the root directory. + var entry = basename ? dir.children[basename] : dir; + + if (!entry) { + if (behavior == goog.fs.DirectoryEntry.Behavior.DEFAULT) { + throw new goog.fs.Error( + {'name': 'NotFoundError'}, + 'loading ' + path + ' from ' + this.getFullPath()); + } else { + goog.asserts.assert( + behavior == goog.fs.DirectoryEntry.Behavior.CREATE || + behavior == goog.fs.DirectoryEntry.Behavior.CREATE_EXCLUSIVE); + entry = createFn(dir, basename); + dir.children[basename] = entry; + this.lastModifiedTimestamp_ = goog.now(); + return entry; + } + } else if (behavior == goog.fs.DirectoryEntry.Behavior.CREATE_EXCLUSIVE) { + throw new goog.fs.Error( + {'name': 'InvalidModificationError'}, + 'loading ' + path + ' from ' + this.getFullPath()); + } else if (entry.isFile() != isFile) { + throw new goog.fs.Error( + {'name': 'TypeMismatchError'}, + 'loading ' + path + ' from ' + this.getFullPath()); + } else { + if (behavior == goog.fs.DirectoryEntry.Behavior.CREATE) { + this.lastModifiedTimestamp_ = goog.now(); + } + return entry; + } +}; + + +/** + * Returns whether this directory has a child with the given name. + * + * @param {string} name The name of the entry to check for. + * @return {boolean} Whether or not this has a child with the given name. + */ +goog.testing.fs.DirectoryEntry.prototype.hasChild = function(name) { + return name in this.children; +}; + + +/** @override */ +goog.testing.fs.DirectoryEntry.prototype.removeRecursively = function() { + var msg = 'removing ' + this.getFullPath() + ' recursively'; + return this.checkNotDeleted(msg).addCallback(function() { + var d = goog.async.Deferred.succeed(null); + goog.object.forEach(this.children, function(child) { + d.awaitDeferred( + child.isDirectory() ? child.removeRecursively() : child.remove()); + }); + d.addCallback(function() { return this.remove(); }, this); + return d; + }); +}; + + +/** @override */ +goog.testing.fs.DirectoryEntry.prototype.listDirectory = function() { + var msg = 'listing ' + this.getFullPath(); + return this.checkNotDeleted(msg).addCallback(function() { + return goog.object.getValues(this.children); + }); +}; + + +/** @override */ +goog.testing.fs.DirectoryEntry.prototype.createPath = + // This isn't really type-safe. + /** @type {!Function} */ (goog.fs.DirectoryEntryImpl.prototype.createPath); + + + +/** + * A mock file entry object. + * + * @param {!goog.testing.fs.FileSystem} fs The filesystem containing this entry. + * @param {!goog.testing.fs.DirectoryEntry} parent The directory entry directly + * containing this entry. + * @param {string} name The name of this entry. + * @param {string} data The data initially contained in the file. + * @param {string=} opt_type The mime type of the blob. + * @constructor + * @extends {goog.testing.fs.Entry} + * @implements {goog.fs.FileEntry} + * @final + */ +goog.testing.fs.FileEntry = function(fs, parent, name, data, opt_type) { + goog.testing.fs.FileEntry.base(this, 'constructor', fs, parent, name); + + /** + * The internal file blob referenced by this file entry. + * @type {!goog.testing.fs.File} + * @private + */ + this.file_ = + new goog.testing.fs.File(name, new Date(goog.now()), data, opt_type); + + /** + * The metadata for file. + * @type {{modificationTime: Date}} + * @private + */ + this.metadata_ = {'modificationTime': this.file_.lastModifiedDate}; +}; +goog.inherits(goog.testing.fs.FileEntry, goog.testing.fs.Entry); + + +/** @override */ +goog.testing.fs.FileEntry.prototype.isFile = function() { + return true; +}; + + +/** @override */ +goog.testing.fs.FileEntry.prototype.isDirectory = function() { + return false; +}; + + +/** @override */ +goog.testing.fs.FileEntry.prototype.clone = function() { + return new goog.testing.fs.FileEntry( + this.getFileSystem(), this.parent, this.getName(), + this.fileSync().toString()); +}; + + +/** @override */ +goog.testing.fs.FileEntry.prototype.getLastModified = function() { + return this.file().addCallback(function(file) { + return file.lastModifiedDate; + }); +}; + + +/** @override */ +goog.testing.fs.FileEntry.prototype.getMetadata = function() { + var msg = 'getting metadata for ' + this.getFullPath(); + return this.checkNotDeleted(msg).addCallback(function() { + return this.metadata_; + }); +}; + + +/** @override */ +goog.testing.fs.FileEntry.prototype.createWriter = function() { + var d = new goog.async.Deferred(); + goog.Timer.callOnce( + goog.bind(d.callback, d, new goog.testing.fs.FileWriter(this))); + return d; +}; + + +/** @override */ +goog.testing.fs.FileEntry.prototype.file = function() { + var msg = 'getting file for ' + this.getFullPath(); + return this.checkNotDeleted(msg).addCallback(function() { + return this.fileSync(); + }); +}; + + +/** + * Get the internal file representation synchronously, without waiting for a + * Deferred to resolve. + * + * @return {!goog.testing.fs.File} The internal file blob referenced by this + * FileEntry. + */ +goog.testing.fs.FileEntry.prototype.fileSync = function() { + return this.file_; +}; diff --git a/closure-library/closure/goog/testing/fs/file.js b/closure-library/closure/goog/testing/fs/file.js new file mode 100644 index 0000000000..334ef36727 --- /dev/null +++ b/closure-library/closure/goog/testing/fs/file.js @@ -0,0 +1,54 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// 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 Mock file object. + * + */ + +goog.setTestOnly('goog.testing.fs.File'); +goog.provide('goog.testing.fs.File'); + +goog.require('goog.testing.fs.Blob'); + + + +/** + * A mock file object. + * + * @param {string} name The name of the file. + * @param {Date=} opt_lastModified The last modified date for this file. May be + * null if file modification dates are not supported. + * @param {string=} opt_data The string data encapsulated by the blob. + * @param {string=} opt_type The mime type of the blob. + * @constructor + * @extends {goog.testing.fs.Blob} + * @final + */ +goog.testing.fs.File = function(name, opt_lastModified, opt_data, opt_type) { + goog.testing.fs.File.base(this, 'constructor', opt_data, opt_type); + + /** + * @see http://www.w3.org/TR/FileAPI/#dfn-name + * @type {string} + */ + this.name = name; + + /** + * @see http://www.w3.org/TR/FileAPI/#dfn-lastModifiedDate + * @type {Date} + */ + this.lastModifiedDate = opt_lastModified || null; +}; +goog.inherits(goog.testing.fs.File, goog.testing.fs.Blob); diff --git a/closure-library/closure/goog/testing/fs/filereader.js b/closure-library/closure/goog/testing/fs/filereader.js new file mode 100644 index 0000000000..9cef767f7c --- /dev/null +++ b/closure-library/closure/goog/testing/fs/filereader.js @@ -0,0 +1,271 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// 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 Mock FileReader object. + * + */ + +goog.setTestOnly('goog.testing.fs.FileReader'); +goog.provide('goog.testing.fs.FileReader'); + +goog.require('goog.Timer'); +goog.require('goog.events.EventTarget'); +goog.require('goog.fs.Error'); +goog.require('goog.fs.FileReader'); +goog.require('goog.testing.fs.Blob'); +goog.require('goog.testing.fs.ProgressEvent'); + + + +/** + * A mock FileReader object. This emits the same events as + * {@link goog.fs.FileReader}. + * + * @constructor + * @extends {goog.events.EventTarget} + */ +goog.testing.fs.FileReader = function() { + goog.testing.fs.FileReader.base(this, 'constructor'); + + /** + * The current state of the reader. + * @type {goog.fs.FileReader.ReadyState} + * @private + */ + this.readyState_ = goog.fs.FileReader.ReadyState.INIT; +}; +goog.inherits(goog.testing.fs.FileReader, goog.events.EventTarget); + + +/** + * The most recent error experienced by this reader. + * @type {goog.fs.Error} + * @private + */ +goog.testing.fs.FileReader.prototype.error_; + + +/** + * Whether the current operation has been aborted. + * @type {boolean} + * @private + */ +goog.testing.fs.FileReader.prototype.aborted_ = false; + + +/** + * The blob this reader is reading from. + * @type {goog.testing.fs.Blob} + * @private + */ +goog.testing.fs.FileReader.prototype.blob_; + + +/** + * The possible return types. + * @enum {number} + */ +goog.testing.fs.FileReader.ReturnType = { + /** + * Used when reading as text. + */ + TEXT: 1, + + /** + * Used when reading as binary string. + */ + BINARY_STRING: 2, + + /** + * Used when reading as array buffer. + */ + ARRAY_BUFFER: 3, + + /** + * Used when reading as data URL. + */ + DATA_URL: 4 +}; + + +/** + * The return type we're reading. + * @type {goog.testing.fs.FileReader.ReturnType} + * @private + */ +goog.testing.fs.FileReader.prototype.returnType_; + + +/** + * @see {goog.fs.FileReader#getReadyState} + * @return {goog.fs.FileReader.ReadyState} The current ready state. + */ +goog.testing.fs.FileReader.prototype.getReadyState = function() { + return this.readyState_; +}; + + +/** + * @see {goog.fs.FileReader#getError} + * @return {goog.fs.Error} The current error. + */ +goog.testing.fs.FileReader.prototype.getError = function() { + return this.error_; +}; + + +/** + * @see {goog.fs.FileReader#abort} + */ +goog.testing.fs.FileReader.prototype.abort = function() { + if (this.readyState_ != goog.fs.FileReader.ReadyState.LOADING) { + var msg = 'aborting read'; + throw new goog.fs.Error({'name': 'InvalidStateError'}, msg); + } + + this.aborted_ = true; +}; + + +/** + * @see {goog.fs.FileReader#getResult} + * @return {*} The result of the file read. + */ +goog.testing.fs.FileReader.prototype.getResult = function() { + if (this.readyState_ != goog.fs.FileReader.ReadyState.DONE) { + return undefined; + } + if (this.error_) { + return undefined; + } + if (this.returnType_ == goog.testing.fs.FileReader.ReturnType.TEXT) { + return this.blob_.toString(); + } else if ( + this.returnType_ == goog.testing.fs.FileReader.ReturnType.ARRAY_BUFFER) { + return this.blob_.toArrayBuffer(); + } else if ( + this.returnType_ == goog.testing.fs.FileReader.ReturnType.BINARY_STRING) { + return this.blob_.toString(); + } else if ( + this.returnType_ == goog.testing.fs.FileReader.ReturnType.DATA_URL) { + return this.blob_.toDataUrl(); + } else { + return undefined; + } +}; + + +/** + * Fires the read events. + * @param {!goog.testing.fs.Blob} blob The blob to read from. + * @private + */ +goog.testing.fs.FileReader.prototype.read_ = function(blob) { + this.blob_ = blob; + if (this.readyState_ == goog.fs.FileReader.ReadyState.LOADING) { + var msg = 'reading file'; + throw new goog.fs.Error({'name': 'InvalidStateError'}, msg); + } + + this.readyState_ = goog.fs.FileReader.ReadyState.LOADING; + goog.Timer.callOnce(function() { + if (this.aborted_) { + this.abort_(blob.size); + return; + } + + this.progressEvent_(goog.fs.FileReader.EventType.LOAD_START, 0, blob.size); + this.progressEvent_( + goog.fs.FileReader.EventType.LOAD, blob.size / 2, blob.size); + this.progressEvent_( + goog.fs.FileReader.EventType.LOAD, blob.size, blob.size); + this.readyState_ = goog.fs.FileReader.ReadyState.DONE; + this.progressEvent_( + goog.fs.FileReader.EventType.LOAD, blob.size, blob.size); + this.progressEvent_( + goog.fs.FileReader.EventType.LOAD_END, blob.size, blob.size); + }, 0, this); +}; + + +/** + * @see {goog.fs.FileReader#readAsBinaryString} + * @param {!goog.testing.fs.Blob} blob The blob to read. + */ +goog.testing.fs.FileReader.prototype.readAsBinaryString = function(blob) { + this.returnType_ = goog.testing.fs.FileReader.ReturnType.BINARY_STRING; + this.read_(blob); +}; + + +/** + * @see {goog.fs.FileReader#readAsArrayBuffer} + * @param {!goog.testing.fs.Blob} blob The blob to read. + */ +goog.testing.fs.FileReader.prototype.readAsArrayBuffer = function(blob) { + this.returnType_ = goog.testing.fs.FileReader.ReturnType.ARRAY_BUFFER; + this.read_(blob); +}; + + +/** + * @see {goog.fs.FileReader#readAsText} + * @param {!goog.testing.fs.Blob} blob The blob to read. + * @param {string=} opt_encoding The name of the encoding to use. + */ +goog.testing.fs.FileReader.prototype.readAsText = function(blob, opt_encoding) { + this.returnType_ = goog.testing.fs.FileReader.ReturnType.TEXT; + this.read_(blob); +}; + + +/** + * @see {goog.fs.FileReader#readAsDataUrl} + * @param {!goog.testing.fs.Blob} blob The blob to read. + */ +goog.testing.fs.FileReader.prototype.readAsDataUrl = function(blob) { + this.returnType_ = goog.testing.fs.FileReader.ReturnType.DATA_URL; + this.read_(blob); +}; + + +/** + * Abort the current action and emit appropriate events. + * + * @param {number} total The total data that was to be processed, in bytes. + * @private + */ +goog.testing.fs.FileReader.prototype.abort_ = function(total) { + this.error_ = new goog.fs.Error({'name': 'AbortError'}, 'reading file'); + this.progressEvent_(goog.fs.FileReader.EventType.ERROR, 0, total); + this.progressEvent_(goog.fs.FileReader.EventType.ABORT, 0, total); + this.readyState_ = goog.fs.FileReader.ReadyState.DONE; + this.progressEvent_(goog.fs.FileReader.EventType.LOAD_END, 0, total); + this.aborted_ = false; +}; + + +/** + * Dispatch a progress event. + * + * @param {goog.fs.FileReader.EventType} type The event type. + * @param {number} loaded The number of bytes processed. + * @param {number} total The total data that was to be processed, in bytes. + * @private + */ +goog.testing.fs.FileReader.prototype.progressEvent_ = function( + type, loaded, total) { + this.dispatchEvent(new goog.testing.fs.ProgressEvent(type, loaded, total)); +}; diff --git a/closure-library/closure/goog/testing/fs/filesystem.js b/closure-library/closure/goog/testing/fs/filesystem.js new file mode 100644 index 0000000000..9fe46c77a2 --- /dev/null +++ b/closure-library/closure/goog/testing/fs/filesystem.js @@ -0,0 +1,65 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// 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 Mock filesystem object. + * + */ + +goog.setTestOnly('goog.testing.fs.FileSystem'); +goog.provide('goog.testing.fs.FileSystem'); + +goog.require('goog.fs.FileSystem'); +goog.require('goog.testing.fs.DirectoryEntry'); + + + +/** + * A mock filesystem object. + * + * @param {string=} opt_name The name of the filesystem. + * @constructor + * @implements {goog.fs.FileSystem} + * @final + */ +goog.testing.fs.FileSystem = function(opt_name) { + /** + * The name of the filesystem. + * @type {string} + * @private + */ + this.name_ = opt_name || 'goog.testing.fs.FileSystem'; + + /** + * The root entry of the filesystem. + * @type {!goog.testing.fs.DirectoryEntry} + * @private + */ + this.root_ = new goog.testing.fs.DirectoryEntry(this, null, '', {}); +}; + + +/** @override */ +goog.testing.fs.FileSystem.prototype.getName = function() { + return this.name_; +}; + + +/** + * @override + * @return {!goog.testing.fs.DirectoryEntry} + */ +goog.testing.fs.FileSystem.prototype.getRoot = function() { + return this.root_; +}; diff --git a/closure-library/closure/goog/testing/fs/filewriter.js b/closure-library/closure/goog/testing/fs/filewriter.js new file mode 100644 index 0000000000..deab7e0f6b --- /dev/null +++ b/closure-library/closure/goog/testing/fs/filewriter.js @@ -0,0 +1,263 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// 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 Mock FileWriter object. + * + */ + +goog.setTestOnly('goog.testing.fs.FileWriter'); +goog.provide('goog.testing.fs.FileWriter'); + +goog.require('goog.Timer'); +goog.require('goog.events.EventTarget'); +goog.require('goog.fs.Error'); +goog.require('goog.fs.FileSaver'); +goog.require('goog.string'); +goog.require('goog.testing.fs.Blob'); +goog.require('goog.testing.fs.File'); +goog.require('goog.testing.fs.ProgressEvent'); +goog.forwardDeclare('goog.testing.fs.FileEntry'); + + + +/** + * A mock FileWriter object. This emits the same events as + * {@link goog.fs.FileSaver} and {@link goog.fs.FileWriter}. + * + * @param {!goog.testing.fs.FileEntry} fileEntry The file entry to write to. + * @constructor + * @extends {goog.events.EventTarget} + * @final + */ +goog.testing.fs.FileWriter = function(fileEntry) { + goog.testing.fs.FileWriter.base(this, 'constructor'); + + /** + * The file entry to which to write. + * @type {!goog.testing.fs.FileEntry} + * @private + */ + this.fileEntry_ = fileEntry; + + /** + * The file blob to write to. + * @type {!goog.testing.fs.File} + * @private + */ + this.file_ = fileEntry.fileSync(); + + /** + * The current state of the writer. + * @type {goog.fs.FileSaver.ReadyState} + * @private + */ + this.readyState_ = goog.fs.FileSaver.ReadyState.INIT; +}; +goog.inherits(goog.testing.fs.FileWriter, goog.events.EventTarget); + + +/** + * The most recent error experienced by this writer. + * @type {goog.fs.Error} + * @private + */ +goog.testing.fs.FileWriter.prototype.error_; + + +/** + * Whether the current operation has been aborted. + * @type {boolean} + * @private + */ +goog.testing.fs.FileWriter.prototype.aborted_ = false; + + +/** + * The current position in the file. + * @type {number} + * @private + */ +goog.testing.fs.FileWriter.prototype.position_ = 0; + + +/** + * @see {goog.fs.FileSaver#getReadyState} + * @return {goog.fs.FileSaver.ReadyState} The ready state. + */ +goog.testing.fs.FileWriter.prototype.getReadyState = function() { + return this.readyState_; +}; + + +/** + * @see {goog.fs.FileSaver#getError} + * @return {goog.fs.Error} The error. + */ +goog.testing.fs.FileWriter.prototype.getError = function() { + return this.error_; +}; + + +/** + * @see {goog.fs.FileWriter#getPosition} + * @return {number} The position. + */ +goog.testing.fs.FileWriter.prototype.getPosition = function() { + return this.position_; +}; + + +/** + * @see {goog.fs.FileWriter#getLength} + * @return {number} The length. + */ +goog.testing.fs.FileWriter.prototype.getLength = function() { + return this.file_.size; +}; + + +/** + * @see {goog.fs.FileSaver#abort} + */ +goog.testing.fs.FileWriter.prototype.abort = function() { + if (this.readyState_ != goog.fs.FileSaver.ReadyState.WRITING) { + var msg = 'aborting save of ' + this.fileEntry_.getFullPath(); + throw new goog.fs.Error({'name': 'InvalidStateError'}, msg); + } + + this.aborted_ = true; +}; + + +/** + * @see {goog.fs.FileWriter#write} + * @param {!goog.testing.fs.Blob} blob The blob to write. + */ +goog.testing.fs.FileWriter.prototype.write = function(blob) { + if (this.readyState_ == goog.fs.FileSaver.ReadyState.WRITING) { + var msg = 'writing to ' + this.fileEntry_.getFullPath(); + throw new goog.fs.Error({'name': 'InvalidStateError'}, msg); + } + + this.readyState_ = goog.fs.FileSaver.ReadyState.WRITING; + goog.Timer.callOnce(function() { + if (this.aborted_) { + this.abort_(blob.size); + return; + } + + this.progressEvent_(goog.fs.FileSaver.EventType.WRITE_START, 0, blob.size); + var fileString = this.file_.toString(); + this.file_.setDataInternal( + fileString.substring(0, this.position_) + blob.toString() + + fileString.substring(this.position_ + blob.size, fileString.length)); + this.position_ += blob.size; + + this.progressEvent_( + goog.fs.FileSaver.EventType.WRITE, blob.size, blob.size); + this.readyState_ = goog.fs.FileSaver.ReadyState.DONE; + this.progressEvent_( + goog.fs.FileSaver.EventType.WRITE_END, blob.size, blob.size); + }, 0, this); +}; + + +/** + * @see {goog.fs.FileWriter#truncate} + * @param {number} size The size to truncate to. + */ +goog.testing.fs.FileWriter.prototype.truncate = function(size) { + if (this.readyState_ == goog.fs.FileSaver.ReadyState.WRITING) { + var msg = 'truncating ' + this.fileEntry_.getFullPath(); + throw new goog.fs.Error({'name': 'InvalidStateError'}, msg); + } + + this.readyState_ = goog.fs.FileSaver.ReadyState.WRITING; + goog.Timer.callOnce(function() { + if (this.aborted_) { + this.abort_(size); + return; + } + + this.progressEvent_(goog.fs.FileSaver.EventType.WRITE_START, 0, size); + + var fileString = this.file_.toString(); + if (size > fileString.length) { + this.file_.setDataInternal( + fileString + goog.string.repeat('\0', size - fileString.length)); + } else { + this.file_.setDataInternal(fileString.substring(0, size)); + } + this.position_ = Math.min(this.position_, size); + + this.progressEvent_(goog.fs.FileSaver.EventType.WRITE, size, size); + this.readyState_ = goog.fs.FileSaver.ReadyState.DONE; + this.progressEvent_(goog.fs.FileSaver.EventType.WRITE_END, size, size); + }, 0, this); +}; + + +/** + * @see {goog.fs.FileWriter#seek} + * @param {number} offset The offset to seek to. + */ +goog.testing.fs.FileWriter.prototype.seek = function(offset) { + if (this.readyState_ == goog.fs.FileSaver.ReadyState.WRITING) { + var msg = 'truncating ' + this.fileEntry_.getFullPath(); + throw new goog.fs.Error({name: 'InvalidStateError'}, msg); + } + + if (offset < 0) { + this.position_ = Math.max(0, this.file_.size + offset); + } else { + this.position_ = Math.min(offset, this.file_.size); + } +}; + + +/** + * Abort the current action and emit appropriate events. + * + * @param {number} total The total data that was to be processed, in bytes. + * @private + */ +goog.testing.fs.FileWriter.prototype.abort_ = function(total) { + this.error_ = new goog.fs.Error( + {'name': 'AbortError'}, 'saving ' + this.fileEntry_.getFullPath()); + this.progressEvent_(goog.fs.FileSaver.EventType.ERROR, 0, total); + this.progressEvent_(goog.fs.FileSaver.EventType.ABORT, 0, total); + this.readyState_ = goog.fs.FileSaver.ReadyState.DONE; + this.progressEvent_(goog.fs.FileSaver.EventType.WRITE_END, 0, total); + this.aborted_ = false; +}; + + +/** + * Dispatch a progress event. + * + * @param {goog.fs.FileSaver.EventType} type The type of the event. + * @param {number} loaded The number of bytes processed. + * @param {number} total The total data that was to be processed, in bytes. + * @private + */ +goog.testing.fs.FileWriter.prototype.progressEvent_ = function( + type, loaded, total) { + // On write, update the last modified date to the current (real or mock) time. + if (type == goog.fs.FileSaver.EventType.WRITE) { + this.file_.lastModifiedDate = new Date(goog.now()); + } + + this.dispatchEvent(new goog.testing.fs.ProgressEvent(type, loaded, total)); +}; diff --git a/closure-library/closure/goog/testing/fs/fs.js b/closure-library/closure/goog/testing/fs/fs.js new file mode 100644 index 0000000000..1fda035e35 --- /dev/null +++ b/closure-library/closure/goog/testing/fs/fs.js @@ -0,0 +1,187 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// 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 Mock implementations of the Closure HTML5 FileSystem wrapper + * classes. These implementations are designed to be usable in any browser, so + * they use none of the native FileSystem-related objects. + * + */ + +goog.setTestOnly('goog.testing.fs'); +goog.provide('goog.testing.fs'); + +goog.require('goog.Timer'); +goog.require('goog.array'); +goog.require('goog.async.Deferred'); +/** @suppress {extraRequire} */ +goog.require('goog.fs'); +goog.require('goog.testing.PropertyReplacer'); +goog.require('goog.testing.fs.Blob'); +goog.require('goog.testing.fs.FileSystem'); + + +/** + * Get a filesystem object. Since these are mocks, there's no difference between + * temporary and persistent filesystems. + * + * @param {number} size Ignored. + * @return {!goog.async.Deferred} The deferred + * {@link goog.testing.fs.FileSystem}. + */ +goog.testing.fs.getTemporary = function(size) { + var d = new goog.async.Deferred(); + goog.Timer.callOnce( + goog.bind(d.callback, d, new goog.testing.fs.FileSystem())); + return d; +}; + + +/** + * Get a filesystem object. Since these are mocks, there's no difference between + * temporary and persistent filesystems. + * + * @param {number} size Ignored. + * @return {!goog.async.Deferred} The deferred + * {@link goog.testing.fs.FileSystem}. + */ +goog.testing.fs.getPersistent = function(size) { + return goog.testing.fs.getTemporary(size); +}; + + +/** + * Which object URLs have been granted for fake blobs. + * @type {!Object} + * @private + */ +goog.testing.fs.objectUrls_ = {}; + + +/** + * Create a fake object URL for a given fake blob. This can be used as a real + * URL, and it can be created and revoked normally. + * + * @param {!goog.testing.fs.Blob} blob The blob for which to create the URL. + * @return {string} The URL. + */ +goog.testing.fs.createObjectUrl = function(blob) { + var url = blob.toDataUrl(); + goog.testing.fs.objectUrls_[url] = true; + return url; +}; + + +/** + * Remove a URL that was created for a fake blob. + * + * @param {string} url The URL to revoke. + */ +goog.testing.fs.revokeObjectUrl = function(url) { + delete goog.testing.fs.objectUrls_[url]; +}; + + +/** + * Return whether or not a URL has been granted for the given blob. + * + * @param {!goog.testing.fs.Blob} blob The blob to check. + * @return {boolean} Whether a URL has been granted. + */ +goog.testing.fs.isObjectUrlGranted = function(blob) { + return (blob.toDataUrl()) in goog.testing.fs.objectUrls_; +}; + + +/** + * Concatenates one or more values together and converts them to a fake blob. + * + * @param {...(string|!goog.testing.fs.Blob)} var_args The values that will make + * up the resulting blob. + * @return {!goog.testing.fs.Blob} The blob. + */ +goog.testing.fs.getBlob = function(var_args) { + return new goog.testing.fs.Blob(goog.array.map(arguments, String).join('')); +}; + + +/** + * Creates a blob with the given properties. + * See https://developer.mozilla.org/en-US/docs/Web/API/Blob for more details. + * + * @param {Array} parts + * The values that will make up the resulting blob. + * @param {string=} opt_type The MIME type of the Blob. + * @param {string=} opt_endings Specifies how strings containing newlines are to + * be written out. + * @return {!goog.testing.fs.Blob} The blob. + */ +goog.testing.fs.getBlobWithProperties = function(parts, opt_type, opt_endings) { + return new goog.testing.fs.Blob( + goog.array.map(parts, String).join(''), opt_type); +}; + + +/** + * Returns the string value of a fake blob. + * + * @param {!goog.testing.fs.Blob} blob The blob to convert to a string. + * @param {string=} opt_encoding Ignored. + * @return {!goog.async.Deferred} The deferred string value of the blob. + */ +goog.testing.fs.blobToString = function(blob, opt_encoding) { + var d = new goog.async.Deferred(); + goog.Timer.callOnce(goog.bind(d.callback, d, blob.toString())); + return d; +}; + + +/** + * Slices the blob. The returned blob contains data from the start byte + * (inclusive) till the end byte (exclusive). Negative indices can be used + * to count bytes from the end of the blob (-1 == blob.size - 1). Indices + * are always clamped to blob range. If end is omitted, all the data till + * the end of the blob is taken. + * + * @param {!goog.testing.fs.Blob} testBlob The blob to slice. + * @param {number} start Index of the starting byte. + * @param {number=} opt_end Index of the ending byte. + * @return {goog.testing.fs.Blob} The new blob or null if not supported. + */ +goog.testing.fs.sliceBlob = function(testBlob, start, opt_end) { + return testBlob.slice(start, opt_end); +}; + + +/** + * Installs goog.testing.fs in place of the standard goog.fs. After calling + * this, code that uses goog.fs should work without issue using goog.testing.fs. + * + * @param {!goog.testing.PropertyReplacer} stubs The property replacer for + * stubbing out the original goog.fs functions. + */ +goog.testing.fs.install = function(stubs) { + // Prevent warnings that goog.fs may get optimized away. It's true this is + // unsafe in compiled code, but it's only meant for tests. + var fs = goog.getObjectByName('goog.fs'); + stubs.replace(fs, 'getTemporary', goog.testing.fs.getTemporary); + stubs.replace(fs, 'getPersistent', goog.testing.fs.getPersistent); + stubs.replace(fs, 'createObjectUrl', goog.testing.fs.createObjectUrl); + stubs.replace(fs, 'revokeObjectUrl', goog.testing.fs.revokeObjectUrl); + stubs.replace(fs, 'getBlob', goog.testing.fs.getBlob); + stubs.replace( + fs, 'getBlobWithProperties', goog.testing.fs.getBlobWithProperties); + stubs.replace(fs, 'blobToString', goog.testing.fs.blobToString); + stubs.replace(fs, 'browserSupportsObjectUrls', function() { return true; }); +}; diff --git a/closure-library/closure/goog/testing/fs/progressevent.js b/closure-library/closure/goog/testing/fs/progressevent.js new file mode 100644 index 0000000000..12d829c94c --- /dev/null +++ b/closure-library/closure/goog/testing/fs/progressevent.js @@ -0,0 +1,86 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// 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 Mock ProgressEvent object. + * + */ + +goog.setTestOnly('goog.testing.fs.ProgressEvent'); +goog.provide('goog.testing.fs.ProgressEvent'); + +goog.require('goog.events.Event'); + +goog.forwardDeclare('goog.fs.FileReader.EventType'); +goog.forwardDeclare('goog.fs.FileSaver.EventType'); + + + +/** + * A mock progress event. + * + * @param {!goog.fs.FileSaver.EventType|!goog.fs.FileReader.EventType} type + * Event type. + * @param {number} loaded The number of bytes processed. + * @param {number} total The total data that was to be processed, in bytes. + * @constructor + * @extends {goog.events.Event} + * @final + */ +goog.testing.fs.ProgressEvent = function(type, loaded, total) { + goog.testing.fs.ProgressEvent.base(this, 'constructor', type); + + /** + * The number of bytes processed. + * @type {number} + * @private + */ + this.loaded_ = loaded; + + + /** + * The total data that was to be procesed, in bytes. + * @type {number} + * @private + */ + this.total_ = total; +}; +goog.inherits(goog.testing.fs.ProgressEvent, goog.events.Event); + + +/** + * @see {goog.fs.ProgressEvent#isLengthComputable} + * @return {boolean} True if the length is known. + */ +goog.testing.fs.ProgressEvent.prototype.isLengthComputable = function() { + return true; +}; + + +/** + * @see {goog.fs.ProgressEvent#getLoaded} + * @return {number} The number of bytes loaded or written. + */ +goog.testing.fs.ProgressEvent.prototype.getLoaded = function() { + return this.loaded_; +}; + + +/** + * @see {goog.fs.ProgressEvent#getTotal} + * @return {number} The total bytes to load or write. + */ +goog.testing.fs.ProgressEvent.prototype.getTotal = function() { + return this.total_; +}; diff --git a/closure-library/closure/goog/testing/functionmock.js b/closure-library/closure/goog/testing/functionmock.js new file mode 100644 index 0000000000..8d5f4d2adf --- /dev/null +++ b/closure-library/closure/goog/testing/functionmock.js @@ -0,0 +1,189 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Enable mocking of functions not attached to objects + * whether they be global / top-level or anonymous methods / closures. + * + * See the unit tests for usage. + * + */ + +goog.setTestOnly('goog.testing'); +goog.provide('goog.testing'); +goog.provide('goog.testing.FunctionMock'); +goog.provide('goog.testing.GlobalFunctionMock'); +goog.provide('goog.testing.MethodMock'); + +goog.require('goog.object'); +goog.require('goog.testing.LooseMock'); +goog.require('goog.testing.Mock'); +goog.require('goog.testing.PropertyReplacer'); +goog.require('goog.testing.StrictMock'); + + +/** + * Class used to mock a function. Useful for mocking closures and anonymous + * callbacks etc. Creates a function object that extends goog.testing.Mock. + * @param {string=} opt_functionName The optional name of the function to mock. + * Set to '[anonymous mocked function]' if not passed in. + * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or + * goog.testing.Mock.STRICT. The default is STRICT. + * @return {!goog.testing.MockInterface} The mocked function. + * @suppress {missingProperties} Mocks do not fit in the type system well. + */ +goog.testing.FunctionMock = function(opt_functionName, opt_strictness) { + var fn = function() { + var args = Array.prototype.slice.call(arguments); + args.splice(0, 0, opt_functionName || '[anonymous mocked function]'); + return fn.$mockMethod.apply(fn, args); + }; + var base = opt_strictness === goog.testing.Mock.LOOSE ? + goog.testing.LooseMock : + goog.testing.StrictMock; + goog.object.extend(fn, new base({})); + + return /** @type {!goog.testing.MockInterface} */ (fn); +}; + + +/** + * Mocks an existing function. Creates a goog.testing.FunctionMock + * and registers it in the given scope with the name specified by functionName. + * @param {Object} scope The scope of the method to be mocked out. + * @param {string} functionName The name of the function we're going to mock. + * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or + * goog.testing.Mock.STRICT. The default is STRICT. + * @return {!goog.testing.MockInterface} The mocked method. + * @suppress {strictMissingProperties} $propertyReplacer_ and $tearDown are + * not defined on goog.testing.MockInterface + */ +goog.testing.MethodMock = function(scope, functionName, opt_strictness) { + if (!(functionName in scope)) { + throw new Error(functionName + ' is not a property of the given scope.'); + } + + var fn = goog.testing.FunctionMock(functionName, opt_strictness); + + fn.$propertyReplacer_ = new goog.testing.PropertyReplacer(); + fn.$propertyReplacer_.set(scope, functionName, fn); + fn.$tearDown = goog.testing.MethodMock.$tearDown; + + return fn; +}; + + +/** + * @private + * @record @extends {goog.testing.MockInterface} + */ +goog.testing.MethodMock.MockInternalInterface_ = function() {}; + +/** @const {!goog.testing.PropertyReplacer} */ +goog.testing.MethodMock.MockInternalInterface_.prototype.$propertyReplacer_; + + +/** + * Resets the global function that we mocked back to its original state. + * @this {goog.testing.MockInterface} + */ +goog.testing.MethodMock.$tearDown = function() { + /** @type {!goog.testing.MethodMock.MockInternalInterface_} */ (this) + .$propertyReplacer_.reset(); +}; + + +/** + * Mocks a global / top-level function. Creates a goog.testing.MethodMock + * in the global scope with the name specified by functionName. + * @param {string} functionName The name of the function we're going to mock. + * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or + * goog.testing.Mock.STRICT. The default is STRICT. + * @return {!goog.testing.MockInterface} The mocked global function. + */ +goog.testing.GlobalFunctionMock = function(functionName, opt_strictness) { + return goog.testing.MethodMock(goog.global, functionName, opt_strictness); +}; + + +/** + * Convenience method for creating a mock for a function. + * @param {string=} opt_functionName The optional name of the function to mock + * set to '[anonymous mocked function]' if not passed in. + * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or + * goog.testing.Mock.STRICT. The default is STRICT. + * @return {!goog.testing.MockInterface} The mocked function. + */ +goog.testing.createFunctionMock = function(opt_functionName, opt_strictness) { + return goog.testing.FunctionMock(opt_functionName, opt_strictness); +}; + + +/** + * Convenience method for creating a mock for a method. + * @param {Object} scope The scope of the method to be mocked out. + * @param {string} functionName The name of the function we're going to mock. + * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or + * goog.testing.Mock.STRICT. The default is STRICT. + * @return {!goog.testing.MockInterface} The mocked global function. + */ +goog.testing.createMethodMock = function(scope, functionName, opt_strictness) { + return goog.testing.MethodMock(scope, functionName, opt_strictness); +}; + + +/** + * Convenience method for creating a mock for a constructor. Copies class + * members to the mock. + * + *

When mocking a constructor to return a mocked instance, remember to create + * the instance mock before mocking the constructor. If you mock the constructor + * first, then the mock framework will be unable to examine the prototype chain + * when creating the mock instance. + * @param {Object} scope The scope of the constructor to be mocked out. + * @param {string} constructorName The name of the constructor we're going to + * mock. + * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or + * goog.testing.Mock.STRICT. The default is STRICT. + * @return {!goog.testing.MockInterface} The mocked constructor. + */ +goog.testing.createConstructorMock = function( + scope, constructorName, opt_strictness) { + var realConstructor = scope[constructorName]; + var constructorMock = + goog.testing.MethodMock(scope, constructorName, opt_strictness); + + // Copy class members from the real constructor to the mock. Do not copy + // the closure superClass_ property (see goog.inherits), the built-in + // prototype property, or properties added to Function.prototype + for (var property in realConstructor) { + if (property != 'superClass_' && property != 'prototype' && + realConstructor.hasOwnProperty(property)) { + constructorMock[property] = realConstructor[property]; + } + } + return constructorMock; +}; + + +/** + * Convenience method for creating a mocks for a global / top-level function. + * @param {string} functionName The name of the function we're going to mock. + * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or + * goog.testing.Mock.STRICT. The default is STRICT. + * @return {!goog.testing.MockInterface} The mocked global function. + */ +goog.testing.createGlobalFunctionMock = function(functionName, opt_strictness) { + return goog.testing.GlobalFunctionMock(functionName, opt_strictness); +}; diff --git a/closure-library/closure/goog/testing/graphics.js b/closure-library/closure/goog/testing/graphics.js new file mode 100644 index 0000000000..eed3bdce25 --- /dev/null +++ b/closure-library/closure/goog/testing/graphics.js @@ -0,0 +1,65 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Testing utilities for DOM related tests. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.setTestOnly('goog.testing.graphics'); +goog.provide('goog.testing.graphics'); + +goog.require('goog.graphics.Path'); +goog.require('goog.testing.asserts'); + + +/** + * Array mapping numeric segment constant to a descriptive character. + * @type {Array} + * @private + */ +goog.testing.graphics.SEGMENT_NAMES_ = function() { + var arr = []; + arr[goog.graphics.Path.Segment.MOVETO] = 'M'; + arr[goog.graphics.Path.Segment.LINETO] = 'L'; + arr[goog.graphics.Path.Segment.CURVETO] = 'C'; + arr[goog.graphics.Path.Segment.ARCTO] = 'A'; + arr[goog.graphics.Path.Segment.CLOSE] = 'X'; + return arr; +}(); + + +/** + * Test if the given path matches the expected array of commands and parameters. + * @param {Array} expected The expected array of commands and + * parameters. + * @param {goog.graphics.Path} path The path to test against. + */ +goog.testing.graphics.assertPathEquals = function(expected, path) { + var actual = []; + path.forEachSegment(function(seg, args) { + actual.push(goog.testing.graphics.SEGMENT_NAMES_[seg]); + Array.prototype.push.apply(actual, args); + }); + assertEquals(expected.length, actual.length); + for (var i = 0; i < expected.length; i++) { + if (goog.isNumber(expected[i])) { + assertTrue(goog.isNumber(actual[i])); + assertRoughlyEquals(expected[i], actual[i], 0.01); + } else { + assertEquals(expected[i], actual[i]); + } + } +}; diff --git a/closure-library/closure/goog/testing/i18n/asserts.js b/closure-library/closure/goog/testing/i18n/asserts.js new file mode 100644 index 0000000000..b6f212b837 --- /dev/null +++ b/closure-library/closure/goog/testing/i18n/asserts.js @@ -0,0 +1,77 @@ +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Assert functions that account for locale data changes. + * + * The locale data gets updated from CLDR (http://cldr.unicode.org/), + * and CLDR gets an update about twice per year. + * So the locale data are expected to change. + * This can make unit tests quite fragile: + * assertEquals("Dec 31, 2013, 1:23pm", format); + * Now imagine that the decision is made to add a dot after abbreviations, + * and a comma between date and time. + * The previous assert will fail, because the string is now + * "Dec. 31 2013, 1:23pm" + * + * One option is to not unit test the results of the formatters client side, + * and just trust that CLDR and closure/i18n takes care of that. + * The other option is to be a more flexible when testing. + * This is the role of assertI18nEquals, to centralize all the small + * differences between hard-coded values in unit tests and the current result. + * It allows some decupling, so that the closure/i18n can be updated without + * breaking all the clients using it. + * For the example above, this will succeed: + * assertI18nEquals("Dec 31, 2013, 1:23pm", "Dec. 31, 2013 1:23pm"); + * It does this by white-listing, no "guessing" involved. + * + * But I would say that the best practice is the first option: trust the + * library, stop unit-testing it. + */ + +goog.provide('goog.testing.i18n.asserts'); +goog.setTestOnly('goog.testing.i18n.asserts'); + +goog.require('goog.testing.jsunit'); + + +/** + * A map of known tests where locale data changed, but the old values are + * still tested for by various clients. + * @const {!Object} + * @private + */ +goog.testing.i18n.asserts.EXPECTED_VALUE_MAP_ = { + // Data to test the assert itself, old string as key, new string as value +}; + + +/** + * Asserts that the two values are "almost equal" from i18n perspective + * (based on a manually maintained and validated whitelist). + * @param {string} expected The expected value. + * @param {string} actual The actual value. + */ +goog.testing.i18n.asserts.assertI18nEquals = function(expected, actual) { + if (expected == actual) { + return; + } + + var newExpected = goog.testing.i18n.asserts.EXPECTED_VALUE_MAP_[expected]; + if (newExpected == actual) { + return; + } + + assertEquals(expected, actual); +}; diff --git a/closure-library/closure/goog/testing/jstdasyncwrapper.js b/closure-library/closure/goog/testing/jstdasyncwrapper.js new file mode 100644 index 0000000000..86916a9413 --- /dev/null +++ b/closure-library/closure/goog/testing/jstdasyncwrapper.js @@ -0,0 +1,370 @@ +// Copyright 2016 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A utility for wrapping a JSTD test object so that any test + * methods are receive a queue that is compatible with JSTD but supports the + * JsUnit async API of returning a promise in the test method. + * + * To convert a JSTD object call convertToAsyncTestObj on it and run with the + * JsUnit test runner. + */ + +goog.provide('goog.testing.JsTdAsyncWrapper'); + +goog.require('goog.Promise'); + + +/** + * @param {Function|string} callback + * @param {number=} opt_delay + * @param {...*} var_args + * @return {number} + * @private + */ +goog.testing.JsTdAsyncWrapper.REAL_SET_TIMEOUT_FN_ = goog.global.setTimeout; + + +/** + * Calls a function after a specified timeout. This uses the original setTimeout + * to be resilient to tests that override it. + * @param {Function} fn The function to call. + * @param {number} timeout Timeout time in ms. + * @private + */ +goog.testing.JsTdAsyncWrapper.REAL_SET_TIMEOUT_ = function(fn, timeout) { + // Setting timeout into a variable is necessary to invoke the function in the + // default global context. Inlining breaks chrome since it requires setTimeout + // to be called with the global context, and IE8 doesn't support the call + // method on setTimeout. + var setTimeoutFn = goog.testing.JsTdAsyncWrapper.REAL_SET_TIMEOUT_FN_; + setTimeoutFn(fn, timeout); +}; + + +/** + * Wraps an object's methods by passing in a Queue that is based on the JSTD + * async API. The queue exposes a promise that resolves when the queue + * completes. This promise can be used in JsUnit tests. + * + * @template T + * @param {T} original The original JSTD test object. The object should + * contain methods such as testXyz or setUp. + * @return {T} A object that has all test methods wrapped in a fake + * testing queue. + */ +goog.testing.JsTdAsyncWrapper.convertToAsyncTestObj = function(original) { + // Wraps a call to a test function and passes an instance of a fake queue + // into the test function. + var queueWrapperFn = function(fn) { + return function() { + var self = /** @type {?} */ (this); // T this is expected + var queue = new goog.testing.JsTdAsyncWrapper.Queue(self); + fn.call(self, queue); + return queue.startExecuting(); + }; + }; + + var newTestObj = {}; + for (var prop in original) { + // If this is a test or tearDown/setUp method wrap the method with a queue + if (prop.indexOf('test') == 0 || prop == 'setUp' || prop == 'tearDown') { + newTestObj[prop] = queueWrapperFn(original[prop]); + } else { + newTestObj[prop] = original[prop]; + } + } + return newTestObj; +}; + + + +/** + * A queue that mirrors the JSTD Async Queue api but exposes a promise that + * resolves once the queue is complete for compatibility with JsUnit. + * @param {!Object} testObj The test object containing all test methods. This + * object is passed into queue callbacks as the "this" object. + * @constructor + * @final + */ +goog.testing.JsTdAsyncWrapper.Queue = function(testObj) { + /** + * The queue steps. + * @private {!Array} + */ + this.steps_ = []; + + /** + * A delegate that is used within a defer call. + * @private {?goog.testing.JsTdAsyncWrapper.Queue} + */ + this.delegate_ = null; + + /** + * thisArg that should be used by default for addCallback function calls. + * @private {!Object} + */ + this.testObj_ = testObj; +}; + + +/** + * @param {string|function(!goog.testing.JsTdAsyncWrapper.Pool_=)} stepName + * The name of the current testing step, or the fn parameter if + * no stepName is desired. + * @param {function(!goog.testing.JsTdAsyncWrapper.Pool_=)=} opt_fn A function + * that will be called. + */ +goog.testing.JsTdAsyncWrapper.Queue.prototype.defer = function( + stepName, opt_fn) { + var fn = opt_fn; + if (!opt_fn && typeof stepName == 'function') { + fn = stepName; + stepName = '(Not named)'; + } + // If another queue.defer is called within a pool callback it should be + // executed after the current one. Any defer that is called within a defer + // will be passed to a delegate and the current defer waits till all delegate + // defer are resolved. + if (this.delegate_) { + this.delegate_.defer(stepName, fn); + return; + } + this.steps_.push(new goog.testing.JsTdAsyncWrapper.Step_( + /** @type {string} */ (stepName), + /** @type {function(!goog.testing.JsTdAsyncWrapper.Pool_=)} */ (fn))); +}; + + +/** + * Starts the execution. + * @return {!goog.Promise} + */ +goog.testing.JsTdAsyncWrapper.Queue.prototype.startExecuting = function() { + return new goog.Promise(goog.bind(function(resolve, reject) { + this.executeNextStep_(resolve, reject); + }, this)); +}; + + +/** + * Executes the next step on the queue waiting for all pool callbacks and then + * starts executing any delegate queues before it finishes. + * @param {function()} callback + * @param {function(*)} errback + * @private + */ +goog.testing.JsTdAsyncWrapper.Queue.prototype.executeNextStep_ = function( + callback, errback) { + // Note: From this point on, we can no longer use goog.Promise (which uses + // the goog.async.run queue) because it conflicts with MockClock, and we can't + // use the native Promise because it is not supported on IE. So we revert to + // using callbacks and setTimeout. + if (!this.steps_.length) { + callback(); + return; + } + var step = this.steps_.shift(); + this.delegate_ = new goog.testing.JsTdAsyncWrapper.Queue(this.testObj_); + var pool = new goog.testing.JsTdAsyncWrapper.Pool_( + this.testObj_, goog.bind(function() { + goog.testing.JsTdAsyncWrapper.REAL_SET_TIMEOUT_(goog.bind(function() { + this.executeDelegate_(callback, errback); + }, this), 0); + }, this), goog.bind(function(reason) { + this.handleError_(errback, reason, step.name); + }, this)); + try { + step.fn.call(this.testObj_, pool); + } catch (e) { + this.handleError_(errback, e, step.name); + } + pool.maybeComplete(); +}; + + +/** + * Execute the delegate queue. + * @param {function()} callback + * @param {function(*)} errback + * @private + */ +goog.testing.JsTdAsyncWrapper.Queue.prototype.executeDelegate_ = function( + callback, errback) { + // Wait till the delegate queue completes before moving on to the + // next step. + if (!this.delegate_) { + this.executeNextStep_(callback, errback); + return; + } + this.delegate_.executeNextStep_(goog.bind(function() { + this.delegate_ = null; + goog.testing.JsTdAsyncWrapper.REAL_SET_TIMEOUT_(goog.bind(function() { + this.executeNextStep_(callback, errback); + }, this), 0); + }, this), errback); +}; + + +/** + * @param {function(*)} errback + * @param {*} reason + * @param {string} stepName + * @private + */ +goog.testing.JsTdAsyncWrapper.Queue.prototype.handleError_ = function( + errback, reason, stepName) { + var error = reason instanceof Error ? reason : Error(reason); + error.message = 'In step ' + stepName + ', error: ' + error.message; + errback(reason); +}; + + + +/** + * A step to be executed. + * @param {string} name + * @param {function(!goog.testing.JsTdAsyncWrapper.Pool_=)} fn + * @constructor + * @private + */ +goog.testing.JsTdAsyncWrapper.Step_ = function(name, fn) { + /** @final {string} */ + this.name = name; + /** @final {function(!goog.testing.JsTdAsyncWrapper.Pool_=)} */ + this.fn = fn; +}; + + + +/** + * A fake pool that mimics the JSTD AsyncTestCase's pool object. + * @param {!Object} testObj The test object containing all test methods. This + * object is passed into queue callbacks as the "this" object. + * @param {function()} callback + * @param {function(*)} errback + * @constructor + * @private + * @final + */ +goog.testing.JsTdAsyncWrapper.Pool_ = function(testObj, callback, errback) { + + /** @private {number} */ + this.outstandingCallbacks_ = 0; + + /** @private {function()} */ + this.callback_ = callback; + + /** @private {function(*)} */ + this.errback_ = errback; + + /** + * thisArg that should be used by default for defer function calls. + * @private {!Object} + */ + this.testObj_ = testObj; + + /** @private {boolean} */ + this.callbackCalled_ = false; +}; + + +/** + * @return {function()} + */ +goog.testing.JsTdAsyncWrapper.Pool_.prototype.noop = function() { + return this.addCallback(function() {}); +}; + + +/** + * @param {function(...*):*} fn The function to add to the pool. + * @param {?number=} opt_n The number of permitted uses of the given callback; + * defaults to one. + * @param {?number=} opt_timeout The timeout in milliseconds. + * This is not supported in the adapter for now. Specifying this argument + * will result in a test failure. + * @param {?string=} opt_description The callback description. + * @return {function()} + */ +goog.testing.JsTdAsyncWrapper.Pool_.prototype.addCallback = function( + fn, opt_n, opt_timeout, opt_description) { + // TODO(mtragut): This could be fixed if required by test cases. + if (opt_timeout || opt_description) { + throw new Error( + 'Setting timeout or description in a pool callback is not supported.'); + } + var numCallbacks = opt_n || 1; + this.outstandingCallbacks_ = this.outstandingCallbacks_ + numCallbacks; + return goog.bind(function() { + try { + fn.apply(this.testObj_, arguments); + } catch (e) { + if (opt_description) { + e.message = opt_description + e.message; + } + this.errback_(e); + } + this.outstandingCallbacks_ = this.outstandingCallbacks_ - 1; + this.maybeComplete(); + }, this); +}; + + +/** + * @param {function(...*):*} fn The function to add to the pool. + * @param {?number=} opt_n The number of permitted uses of the given callback; + * defaults to one. + * @param {?number=} opt_timeout The timeout in milliseconds. + * This is not supported in the adapter for now. Specifying this argument + * will result in a test failure. + * @param {?string=} opt_description The callback description. + * @return {function()} + */ +goog.testing.JsTdAsyncWrapper.Pool_.prototype.add = + goog.testing.JsTdAsyncWrapper.Pool_.prototype.addCallback; + + +/** + * @param {string} msg The message to print if the error callback gets called. + * @return {function()} + */ +goog.testing.JsTdAsyncWrapper.Pool_.prototype.addErrback = function(msg) { + return goog.bind(function() { + var errorMsg = msg; + if (arguments.length) { + errorMsg += ' - Error callback called with params: ( '; + for (var i = 0; i < arguments.length; i++) { + var arg = arguments[i]; + errorMsg += arg + ' '; + if (arg instanceof Error) { + errorMsg += '\n' + arg.stack + '\n'; + } + } + errorMsg += ')'; + } + this.errback_(errorMsg); + }, this); +}; + + +/** + * Completes the pool if there are no outstanding callbacks. + */ +goog.testing.JsTdAsyncWrapper.Pool_.prototype.maybeComplete = function() { + if (this.outstandingCallbacks_ == 0 && !this.callbackCalled_) { + this.callbackCalled_ = true; + this.callback_(); + } +}; diff --git a/closure-library/closure/goog/testing/jstdtestcaseadapter.js b/closure-library/closure/goog/testing/jstdtestcaseadapter.js new file mode 100644 index 0000000000..ea9014469c --- /dev/null +++ b/closure-library/closure/goog/testing/jstdtestcaseadapter.js @@ -0,0 +1,161 @@ +// 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 Conditionally add "adapter" methods to allow JSTD test cases + * to run under the Closure Test Runner. The goal is to allow tests + * to function regardless of the environment they are running under to allow + * them to transition to the Closure test runner and allow JSTD runner to be + * deprecated. + */ +goog.setTestOnly('goog.testing.JsTdTestCaseAdapter'); +goog.provide('goog.testing.JsTdTestCaseAdapter'); + +goog.require('goog.async.run'); +goog.require('goog.functions'); +goog.require('goog.testing.JsTdAsyncWrapper'); +goog.require('goog.testing.TestCase'); +goog.require('goog.testing.jsunit'); + + +/** + * @param {string} testCaseName The name of the test case. + * @param {function(): boolean} condition A condition to determine whether to + * run the tests. + * @param {?=} opt_proto An optional prototype object for the test case. + * @param {boolean=} opt_isAsync Whether this test is an async test using the + * JSTD testing queue. + * @return {!Function} + * @private + * @suppress {checkPrototypalTypes} + */ +goog.testing.JsTdTestCaseAdapter.TestCaseFactory_ = function( + testCaseName, condition, opt_proto, opt_isAsync) { + /** @constructor */ + var T = function() {}; + if (opt_proto) T.prototype = opt_proto; + T.displayName = testCaseName; + + goog.async.run(function() { + var t = new T(); + if (opt_isAsync) { + t = goog.testing.JsTdAsyncWrapper.convertToAsyncTestObj(t); + } + var testCase = new goog.testing.TestCase(testCaseName); + testCase.shouldRunTests = condition; + testCase.setTestObj(t); + testCase.autoDiscoverTests(); + goog.testing.TestCase.initializeTestRunner(testCase, undefined); + }); + + return T; +}; + + +/** + * @param {string} testCaseName The name of the test case. + * @param {?=} opt_proto An optional prototype object for the test case. + * @return {!Function} + * @private + */ +goog.testing.JsTdTestCaseAdapter.TestCase_ = function(testCaseName, opt_proto) { + return goog.testing.JsTdTestCaseAdapter.TestCaseFactory_( + testCaseName, goog.functions.TRUE, opt_proto); +}; + + +/** + * @param {string} testCaseName The name of the test case. + * @param {function(): boolean} condition A condition to determine whether to + * run the tests. + * @param {?=} opt_proto An optional prototype object for the test case. + * @return {!Function} + * @private + */ +goog.testing.JsTdTestCaseAdapter.ConditionalTestCase_ = function( + testCaseName, condition, opt_proto) { + return goog.testing.JsTdTestCaseAdapter.TestCaseFactory_( + testCaseName, condition, opt_proto); +}; + + +/** + * @param {string} testCaseName The name of the test case. + * @param {?=} opt_proto An optional prototype object for the test case. + * @return {!Function} + * @private + */ +goog.testing.JsTdTestCaseAdapter.AsyncTestCase_ = function( + testCaseName, opt_proto) { + return goog.testing.JsTdTestCaseAdapter.TestCaseFactory_( + testCaseName, goog.functions.TRUE, opt_proto, true); +}; + + +/** + * @param {string} testCaseName The name of the test case. + * @param {function(): boolean} condition A condition to determine whether to + * run the tests. + * @param {?=} opt_proto An optional prototype object for the test case. + * @return {!Function} + * @private + */ +goog.testing.JsTdTestCaseAdapter.AsyncConditionalTestCase_ = function( + testCaseName, condition, opt_proto) { + return goog.testing.JsTdTestCaseAdapter.TestCaseFactory_( + testCaseName, condition, opt_proto, true); +}; + + +// --- conditionally add polyfills for the basic JSTD API --- + + +/** @suppress {duplicate} */ +var TestCase = TestCase || goog.testing.JsTdTestCaseAdapter.TestCase_; + + +/** @suppress {duplicate} */ +var ConditionalTestCase = ConditionalTestCase || + goog.testing.JsTdTestCaseAdapter.ConditionalTestCase_; + + +/** @suppress {duplicate} */ +var AsyncTestCase = + AsyncTestCase || goog.testing.JsTdTestCaseAdapter.AsyncTestCase_; + + +/** @suppress {duplicate} */ +var AsyncConditionalTestCase = AsyncConditionalTestCase || + goog.testing.JsTdTestCaseAdapter.AsyncConditionalTestCase_; + + +/** @suppress {duplicate} */ +var ConditionalAsyncTestCase = ConditionalAsyncTestCase || + goog.testing.JsTdTestCaseAdapter.AsyncConditionalTestCase_; + + +// The API is also available under the jstestdriver namespace. + +/** @suppress {duplicate} */ +var jstestdriver = jstestdriver || {}; +if (!jstestdriver.testCaseManager) { + /** A jstestdriver API polyfill. */ + jstestdriver.testCaseManager = { + TestCase: TestCase, + ConditionalTestCase: ConditionalTestCase, + AsyncTestCase: AsyncTestCase, + AsyncConditionalTestCase: AsyncConditionalTestCase, + ConditionalAsyncTestCase: ConditionalAsyncTestCase + }; +} diff --git a/closure-library/closure/goog/testing/jsunit.js b/closure-library/closure/goog/testing/jsunit.js new file mode 100644 index 0000000000..55582ab584 --- /dev/null +++ b/closure-library/closure/goog/testing/jsunit.js @@ -0,0 +1,196 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities for working with JsUnit. Writes out the JsUnit file + * that needs to be included in every unit test. + * + * Testing code should not have dependencies outside of goog.testing so as to + * reduce the chance of masking missing dependencies. + * + */ + +goog.setTestOnly('goog.testing.jsunit'); +goog.provide('goog.testing.jsunit'); + +goog.require('goog.dom.TagName'); +goog.require('goog.testing.TestCase'); +goog.require('goog.testing.TestRunner'); +goog.require('goog.userAgent'); + + +/** + * @define {boolean} If this code is being parsed by JsTestC, we let it disable + * the onload handler to avoid running the test in JsTestC. + */ +goog.define('goog.testing.jsunit.AUTO_RUN_ONLOAD', true); + + +/** + * @define {number} Sets a delay in milliseconds after the window onload event + * and running the tests. Used as a workaround for IE failing to report load + * event if the page has iframes. The appropriate value is zero; + * maximum should be 500. Do not use this value to support asynchronous tests. + */ +goog.define('goog.testing.jsunit.AUTO_RUN_DELAY_IN_MS', 0); + + +(function() { + // Only allow one global test runner to be created on a page. + if (goog.global['G_testRunner'] instanceof goog.testing.TestRunner) { + return; + } + + // Increases the maximum number of stack frames in Google Chrome from the + // default 10 to 50 to get more useful stack traces. + Error.stackTraceLimit = 50; + + // Store a reference to the window's timeout so that it can't be overridden + // by tests. + /** @type {!Function} */ + var realTimeout = window.setTimeout; + + // Create a test runner. + var tr = new goog.testing.TestRunner(); + + // Export it so that it can be queried by Selenium and tests that use a + // compiled test runner. + goog.exportSymbol('G_testRunner', tr); + goog.exportSymbol('G_testRunner.initialize', tr.initialize); + goog.exportSymbol('G_testRunner.isInitialized', tr.isInitialized); + goog.exportSymbol('G_testRunner.isFinished', tr.isFinished); + goog.exportSymbol('G_testRunner.getUniqueId', tr.getUniqueId); + goog.exportSymbol('G_testRunner.isSuccess', tr.isSuccess); + goog.exportSymbol('G_testRunner.getReport', tr.getReport); + goog.exportSymbol('G_testRunner.getRunTime', tr.getRunTime); + goog.exportSymbol('G_testRunner.getNumFilesLoaded', tr.getNumFilesLoaded); + goog.exportSymbol('G_testRunner.setStrict', tr.setStrict); + goog.exportSymbol('G_testRunner.logTestFailure', tr.logTestFailure); + goog.exportSymbol('G_testRunner.getTestResults', tr.getTestResults); + goog.exportSymbol( + 'G_testRunner.getTestResultsAsJson', tr.getTestResultsAsJson); + + // Export debug as a global function for JSUnit compatibility. This just + // calls log on the current test case. + if (!goog.global['debug']) { + goog.exportSymbol('debug', goog.bind(tr.log, tr)); + } + + // If the application has defined a global error filter, set it now. This + // allows users who use a base test include to set the error filter before + // the testing code is loaded. + if (goog.global['G_errorFilter']) { + tr.setErrorFilter(goog.global['G_errorFilter']); + } + + var maybeGetStack = function(error) { + if (typeof error == 'object') { + var stack = error.stack; + if (stack && typeof stack == 'string') { + // non-empty string + return stack; + } + } + return ''; + }; + + // Add an error handler to report errors that may occur during + // initialization of the page. + var onerror = window.onerror; + window.onerror = function(messageOrEvent, url, line) { + // TODO(johnlenz): fix this function parameters once the "onerror" + // definition has been corrected. + // colno and errObj were added later. + var colno = arguments[3]; + var errObj = arguments[4]; + // Call any existing onerror handlers, except our boot handler. + if (onerror && onerror != window["__onerror_at_boot"]) { + onerror.apply(window, arguments); + } + var stack = maybeGetStack(errObj || messageOrEvent); + if (stack) { + tr.logError(stack); + } else if (typeof messageOrEvent == 'object') { + var error = messageOrEvent; + // Some older webkit browsers pass an event object as the only argument + // to window.onerror. It doesn't contain an error message, url or line + // number. We therefore log as much info as we can. + if (error.target && error.target.tagName == goog.dom.TagName.SCRIPT) { + tr.logError('UNKNOWN ERROR: Script ' + error.target.src); + } else { + tr.logError('UNKNOWN ERROR: No error information available.'); + } + } else { + // Add the column if it is available, older browsers won't have it. + var colstr = colno != null ? '\nColumn: ' + colno : ''; + tr.logError( + 'JS ERROR: ' + messageOrEvent + '\nURL: ' + url + '\nLine: ' + line + + colstr); + } + }; + + /** + * The onerror handler that may have been set by the test runner. + * @type {?function(string, string=, number=, number=, Object=)} + */ + window["__onerror_at_boot"] = window["__onerror_at_boot"] || null; + /** + * The arguments for any call to window.onerror occuring before this point. + * @type {Array>} */ + window["__errors_since_boot"] = window["__errors_since_boot"] || null; + + if (window["__onerror_at_boot"]) { + if (window['__errors_since_boot']) { + for (var i = 0; i < window['__errors_since_boot'].length; i++) { + var args = window['__errors_since_boot'][i]; + window.onerror.apply(window, args); + } + } + // http://perfectionkills.com/understanding-delete/#ie_bugs + window["__onerror_at_boot"] = null; + } + + // Create an onload handler, if the test runner hasn't been initialized then + // no test has been registered with the test runner by the test file. We + // then create a new test case and auto discover any tests in the global + // scope. If this code is being parsed by JsTestC, we let it disable the + // onload handler to avoid running the test in JsTestC. + if (goog.testing.jsunit.AUTO_RUN_ONLOAD) { + var onload = window.onload; + window.onload = function(e) { + // Call any existing onload handlers. + if (onload) { + onload(e); + } + // Execute the test on the next turn, to allow the WebDriver.get() + // operation to return to the test runner and begin polling. + var executionDelayAfterLoad = goog.testing.jsunit.AUTO_RUN_DELAY_IN_MS; + if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('11')) { + // Older IE Webdriver will not return onload if the page uses iframes. + executionDelayAfterLoad = + Math.max(goog.testing.jsunit.AUTO_RUN_DELAY_IN_MS, 500); + } + + realTimeout(function() { + if (!tr.initialized) { + var testCase = new goog.testing.TestCase(document.title); + goog.testing.TestCase.initializeTestCase(testCase); + tr.initialize(testCase); + } + tr.execute(); + }, executionDelayAfterLoad); + window.onload = null; + }; + } +})(); diff --git a/closure-library/closure/goog/testing/jsunitexception.js b/closure-library/closure/goog/testing/jsunitexception.js new file mode 100644 index 0000000000..c4714a7b4c --- /dev/null +++ b/closure-library/closure/goog/testing/jsunitexception.js @@ -0,0 +1,50 @@ +// Copyright 2017 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.testing.JsUnitException'); +goog.setTestOnly(); + +goog.require('goog.testing.stacktrace'); + + +/** + * @param {string} comment A summary for the exception. + * @param {?string=} opt_message A description of the exception. + * @constructor + * @extends {Error} + * @final + */ +goog.testing.JsUnitException = function(comment, opt_message) { + this.isJsUnitException = true; + this.message = (comment ? comment : '') + + (comment && opt_message ? '\n' : '') + (opt_message ? opt_message : ''); + this.stackTrace = goog.testing.stacktrace.get(); + // These fields are for compatibility with jsUnitTestManager. + this.comment = comment || null; + this.jsUnitMessage = opt_message || ''; + + // Ensure there is a stack trace. + if (Error.captureStackTrace) { + Error.captureStackTrace(this, goog.testing.JsUnitException); + } else { + this.stack = new Error().stack || ''; + } +}; +goog.inherits(goog.testing.JsUnitException, Error); + + +/** @override */ +goog.testing.JsUnitException.prototype.toString = function() { + return this.message; +}; diff --git a/closure-library/closure/goog/testing/loosemock.js b/closure-library/closure/goog/testing/loosemock.js new file mode 100644 index 0000000000..a651562c2e --- /dev/null +++ b/closure-library/closure/goog/testing/loosemock.js @@ -0,0 +1,294 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview This file defines a loose mock implementation. + */ + +goog.setTestOnly('goog.testing.LooseExpectationCollection'); +goog.provide('goog.testing.LooseExpectationCollection'); +goog.provide('goog.testing.LooseMock'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.structs.Map'); +goog.require('goog.structs.Set'); +goog.require('goog.testing.Mock'); + + + +/** + * This class is an ordered collection of expectations for one method. Since + * the loose mock does most of its verification at the time of $verify, this + * class is necessary to manage the return/throw behavior when the mock is + * being called. + * @constructor + * @final + */ +goog.testing.LooseExpectationCollection = function() { + /** + * The list of expectations. All of these should have the same name. + * @type {!Array} + * @private + */ + this.expectations_ = []; +}; + + +/** + * Adds an expectation to this collection. + * @param {!goog.testing.MockExpectation} expectation The expectation to add. + */ +goog.testing.LooseExpectationCollection.prototype.addExpectation = function( + expectation) { + this.expectations_.push(expectation); +}; + + +/** + * Gets the list of expectations in this collection. + * @return {!Array} The array of expectations. + */ +goog.testing.LooseExpectationCollection.prototype.getExpectations = function() { + return this.expectations_; +}; + + + +/** + * This is a mock that does not care about the order of method calls. As a + * result, it won't throw exceptions until verify() is called. The only + * exception is that if a method is called that has no expectations, then an + * exception will be thrown. + * @param {Object|Function} objectToMock The object that should be mocked, or + * the constructor of an object to mock. + * @param {boolean=} opt_ignoreUnexpectedCalls Whether to ignore unexpected + * calls. + * @param {boolean=} opt_mockStaticMethods An optional argument denoting that + * a mock should be constructed from the static functions of a class. + * @param {boolean=} opt_createProxy An optional argument denoting that + * a proxy for the target mock should be created. + * @constructor + * @extends {goog.testing.Mock} + */ +goog.testing.LooseMock = function( + objectToMock, opt_ignoreUnexpectedCalls, opt_mockStaticMethods, + opt_createProxy) { + goog.testing.Mock.call( + this, objectToMock, opt_mockStaticMethods, opt_createProxy); + + /** + * A map of method names to a LooseExpectationCollection for that method. + * @type {!goog.structs.Map} + * @private + */ + this.$expectations_ = new goog.structs.Map(); + + /** @private {!goog.structs.Set} */ + this.awaitingExpectations_ = new goog.structs.Set(); + + /** + * The calls that have been made; we cache them to verify at the end. Each + * element is an array where the first element is the name, and the second + * element is the arguments. + * @type {Array>} + * @private + */ + this.$calls_ = []; + + /** + * Whether to ignore unexpected calls. + * @type {boolean} + * @private + */ + this.$ignoreUnexpectedCalls_ = !!opt_ignoreUnexpectedCalls; +}; +goog.inherits(goog.testing.LooseMock, goog.testing.Mock); + + +/** + * A setter for the ignoreUnexpectedCalls field. + * @param {boolean} ignoreUnexpectedCalls Whether to ignore unexpected calls. + * @return {!goog.testing.LooseMock} This mock object. + */ +goog.testing.LooseMock.prototype.$setIgnoreUnexpectedCalls = function( + ignoreUnexpectedCalls) { + this.$ignoreUnexpectedCalls_ = ignoreUnexpectedCalls; + return this; +}; + + +/** @override */ +goog.testing.LooseMock.prototype.$recordExpectation = function() { + if (!this.$expectations_.containsKey(this.$pendingExpectation.name)) { + this.$expectations_.set( + this.$pendingExpectation.name, + new goog.testing.LooseExpectationCollection()); + } + + var collection = this.$expectations_.get(this.$pendingExpectation.name); + collection.addExpectation(this.$pendingExpectation); + if (this.$pendingExpectation) { + this.awaitingExpectations_.add(this.$pendingExpectation); + } +}; + + +/** @override */ +goog.testing.LooseMock.prototype.$recordCall = function(name, args) { + if (!this.$expectations_.containsKey(name)) { + if (this.$ignoreUnexpectedCalls_) { + return; + } + this.$throwCallException(name, args); + } + + // Start from the beginning of the expectations for this name, + // and iterate over them until we find an expectation that matches + // and also has calls remaining. + var collection = this.$expectations_.get(name); + var matchingExpectation = null; + var expectations = collection.getExpectations(); + for (var i = 0; i < expectations.length; i++) { + var expectation = expectations[i]; + if (this.$verifyCall(expectation, name, args)) { + matchingExpectation = expectation; + if (expectation.actualCalls < expectation.maxCalls) { + break; + } // else continue and see if we can find something that does match + } + } + if (matchingExpectation == null) { + this.$throwCallException(name, args, expectation); + } + + matchingExpectation.actualCalls++; + if (matchingExpectation.actualCalls > matchingExpectation.maxCalls) { + this.$throwException( + 'Too many calls to ' + matchingExpectation.name + '\nExpected: ' + + matchingExpectation.maxCalls + ' but was: ' + + matchingExpectation.actualCalls); + } + if (matchingExpectation.actualCalls >= matchingExpectation.minCalls) { + this.awaitingExpectations_.remove(matchingExpectation); + this.maybeFinishedWithExpectations_(); + } + + this.$calls_.push([name, args]); + return this.$do(matchingExpectation, args); +}; + + +/** @override */ +goog.testing.LooseMock.prototype.$reset = function() { + goog.testing.LooseMock.superClass_.$reset.call(this); + + this.$expectations_ = new goog.structs.Map(); + this.awaitingExpectations_ = new goog.structs.Set(); + this.$calls_ = []; +}; + + +/** @override */ +goog.testing.LooseMock.prototype.$replay = function() { + goog.testing.LooseMock.superClass_.$replay.call(this); + + // Verify that there are no expectations that can never be reached. + // This can't catch every situation, but it is a decent sanity check + // and it's similar to the behavior of EasyMock in java. + var collections = this.$expectations_.getValues(); + for (var i = 0; i < collections.length; i++) { + var expectations = collections[i].getExpectations(); + for (var j = 0; j < expectations.length; j++) { + var expectation = expectations[j]; + // If this expectation can be called infinite times, then + // check if any subsequent expectation has the exact same + // argument list. + if (!isFinite(expectation.maxCalls)) { + for (var k = j + 1; k < expectations.length; k++) { + var laterExpectation = expectations[k]; + if (laterExpectation.minCalls > 0 && + goog.array.equals( + expectation.argumentList, laterExpectation.argumentList)) { + var name = expectation.name; + var argsString = this.$argumentsAsString(expectation.argumentList); + this.$throwException([ + 'Expected call to ', name, ' with arguments ', argsString, + ' has an infinite max number of calls; can\'t expect an', + ' identical call later with a positive min number of calls' + ].join('')); + } + } + } + } + } +}; + + +/** @override */ +goog.testing.LooseMock.prototype.$waitAndVerify = function() { + var keys = this.$expectations_.getKeys(); + for (var i = 0; i < keys.length; i++) { + var expectations = this.$expectations_.get(keys[i]).getExpectations(); + for (var j = 0; j < expectations.length; j++) { + var expectation = expectations[j]; + goog.asserts.assert( + !isFinite(expectation.maxCalls) || + expectation.minCalls == expectation.maxCalls, + 'Mock expectations cannot have a loose number of expected calls to ' + + 'use $waitAndVerify.'); + } + } + var promise = goog.testing.LooseMock.base(this, '$waitAndVerify'); + this.maybeFinishedWithExpectations_(); + return promise; +}; + +/** + * @private + */ +goog.testing.LooseMock.prototype.maybeFinishedWithExpectations_ = function() { + var unresolvedExpectations = goog.array.some( + this.$expectations_.getValues(), function(expectationCollection) { + return goog.array.some( + expectationCollection.getExpectations(), function(expectation) { + return expectation.actualCalls < expectation.minCalls; + }); + }); + if (this.waitingForExpectations && !unresolvedExpectations) { + this.waitingForExpectations.resolve(); + } +}; + +/** @override */ +goog.testing.LooseMock.prototype.$verify = function() { + goog.testing.LooseMock.superClass_.$verify.call(this); + var collections = this.$expectations_.getValues(); + + for (var i = 0; i < collections.length; i++) { + var expectations = collections[i].getExpectations(); + for (var j = 0; j < expectations.length; j++) { + var expectation = expectations[j]; + if (expectation.actualCalls > expectation.maxCalls) { + this.$throwException( + 'Too many calls to ' + expectation.name + '\nExpected: ' + + expectation.maxCalls + ' but was: ' + expectation.actualCalls); + } else if (expectation.actualCalls < expectation.minCalls) { + this.$throwException( + 'Not enough calls to ' + expectation.name + '\nExpected: ' + + expectation.minCalls + ' but was: ' + expectation.actualCalls); + } + } + } +}; diff --git a/closure-library/closure/goog/testing/messaging/mockmessagechannel.js b/closure-library/closure/goog/testing/messaging/mockmessagechannel.js new file mode 100644 index 0000000000..594d0a1f04 --- /dev/null +++ b/closure-library/closure/goog/testing/messaging/mockmessagechannel.js @@ -0,0 +1,82 @@ +// Copyright 2010 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Mock MessageChannel implementation that can receive fake + * messages and test that the right messages are sent. + * + */ + + +goog.setTestOnly('goog.testing.messaging.MockMessageChannel'); +goog.provide('goog.testing.messaging.MockMessageChannel'); + +goog.require('goog.messaging.AbstractChannel'); +goog.require('goog.testing.MockControl'); +goog.require('goog.testing.asserts'); + + + +/** + * Class for unit-testing code that communicates over a MessageChannel. + * @param {goog.testing.MockControl} mockControl The mock control used to create + * the method mock for #send. + * @extends {goog.messaging.AbstractChannel} + * @constructor + * @final + */ +goog.testing.messaging.MockMessageChannel = function(mockControl) { + goog.testing.messaging.MockMessageChannel.base(this, 'constructor'); + + /** + * Whether the channel has been disposed. + * @type {boolean} + */ + this.disposed = false; + + mockControl.createMethodMock(this, 'send'); +}; +goog.inherits( + goog.testing.messaging.MockMessageChannel, goog.messaging.AbstractChannel); + + +/** + * A mock send function. Actually an instance of + * {@link goog.testing.FunctionMock}. + * @param {string} serviceName The name of the remote service to run. + * @param {string|!Object} payload The payload to send to the remote page. + * @override + */ +goog.testing.messaging.MockMessageChannel.prototype.send = function( + serviceName, payload) {}; + + +/** + * Sets a flag indicating that this is disposed. + * @override + */ +goog.testing.messaging.MockMessageChannel.prototype.dispose = function() { + this.disposed = true; +}; + + +/** + * Mocks the receipt of a message. Passes the payload the appropriate service. + * @param {string} serviceName The service to run. + * @param {string|!Object} payload The argument to pass to the service. + */ +goog.testing.messaging.MockMessageChannel.prototype.receive = function( + serviceName, payload) { + this.deliver(serviceName, payload); +}; diff --git a/closure-library/closure/goog/testing/messaging/mockmessageevent.js b/closure-library/closure/goog/testing/messaging/mockmessageevent.js new file mode 100644 index 0000000000..da76cca2b6 --- /dev/null +++ b/closure-library/closure/goog/testing/messaging/mockmessageevent.js @@ -0,0 +1,103 @@ +// Copyright 2010 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A simple mock class for imitating HTML5 MessageEvents. + * + */ + +goog.setTestOnly('goog.testing.messaging.MockMessageEvent'); +goog.provide('goog.testing.messaging.MockMessageEvent'); + +goog.require('goog.events.BrowserEvent'); +goog.require('goog.events.EventType'); +goog.require('goog.testing.events.Event'); + + + +/** + * Creates a new fake MessageEvent. + * + * @param {*} data The data of the message. + * @param {string=} opt_origin The origin of the message, for server-sent and + * cross-document events. + * @param {string=} opt_lastEventId The last event ID, for server-sent events. + * @param {Window=} opt_source The proxy for the source window, for + * cross-document events. + * @param {Array=} opt_ports The Array of ports sent with the + * message, for cross-document and channel events. + * @extends {goog.testing.events.Event} + * @constructor + * @final + */ +goog.testing.messaging.MockMessageEvent = function( + data, opt_origin, opt_lastEventId, opt_source, opt_ports) { + goog.testing.messaging.MockMessageEvent.base( + this, 'constructor', goog.events.EventType.MESSAGE); + + /** + * The data of the message. + * @type {*} + */ + this.data = data; + + /** + * The origin of the message, for server-sent and cross-document events. + * @type {?string} + */ + this.origin = opt_origin || null; + + /** + * The last event ID, for server-sent events. + * @type {?string} + */ + this.lastEventId = opt_lastEventId || null; + + /** + * The proxy for the source window, for cross-document events. + * @type {Window} + */ + this.source = opt_source || null; + + /** + * The Array of ports sent with the message, for cross-document and channel + * events. + * @type {Array} + */ + this.ports = opt_ports || null; +}; +goog.inherits( + goog.testing.messaging.MockMessageEvent, goog.testing.events.Event); + + +/** + * Wraps a new fake MessageEvent in a BrowserEvent, like how a real MessageEvent + * would be wrapped. + * + * @param {*} data The data of the message. + * @param {string=} opt_origin The origin of the message, for server-sent and + * cross-document events. + * @param {string=} opt_lastEventId The last event ID, for server-sent events. + * @param {Window=} opt_source The proxy for the source window, for + * cross-document events. + * @param {Array=} opt_ports The Array of ports sent with the + * message, for cross-document and channel events. + * @return {!goog.events.BrowserEvent} The wrapping event. + */ +goog.testing.messaging.MockMessageEvent.wrap = function( + data, opt_origin, opt_lastEventId, opt_source, opt_ports) { + return new goog.events.BrowserEvent( + new goog.testing.messaging.MockMessageEvent( + data, opt_origin, opt_lastEventId, opt_source, opt_ports)); +}; diff --git a/closure-library/closure/goog/testing/messaging/mockmessageport.js b/closure-library/closure/goog/testing/messaging/mockmessageport.js new file mode 100644 index 0000000000..3614fe9e26 --- /dev/null +++ b/closure-library/closure/goog/testing/messaging/mockmessageport.js @@ -0,0 +1,88 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// 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 simple dummy class for representing message ports in tests. + * + */ + +goog.setTestOnly('goog.testing.messaging.MockMessagePort'); +goog.provide('goog.testing.messaging.MockMessagePort'); + +goog.require('goog.events.EventTarget'); +goog.require('goog.testing.MockControl'); + + + +/** + * Class for unit-testing code that uses MessagePorts. + * @param {*} id An opaque identifier, used because message ports otherwise have + * no distinguishing characteristics. + * @param {goog.testing.MockControl} mockControl The mock control used to create + * the method mock for #postMessage. + * @constructor + * @extends {goog.events.EventTarget} + * @final + */ +goog.testing.messaging.MockMessagePort = function(id, mockControl) { + goog.testing.messaging.MockMessagePort.base(this, 'constructor'); + + /** + * An opaque identifier, used because message ports otherwise have no + * distinguishing characteristics. + * @type {*} + */ + this.id = id; + + /** + * Whether or not the port has been started. + * @type {boolean} + */ + this.started = false; + + /** + * Whether or not the port has been closed. + * @type {boolean} + */ + this.closed = false; + + mockControl.createMethodMock(this, 'postMessage'); +}; +goog.inherits(goog.testing.messaging.MockMessagePort, goog.events.EventTarget); + + +/** + * A mock postMessage funciton. Actually an instance of + * {@link goog.testing.FunctionMock}. + * @param {*} message The message to send. + * @param {Array=} opt_ports Ports to send with the message. + */ +goog.testing.messaging.MockMessagePort.prototype.postMessage = function( + message, opt_ports) {}; + + +/** + * Starts the port. + */ +goog.testing.messaging.MockMessagePort.prototype.start = function() { + this.started = true; +}; + + +/** + * Closes the port. + */ +goog.testing.messaging.MockMessagePort.prototype.close = function() { + this.closed = true; +}; diff --git a/closure-library/closure/goog/testing/messaging/mockportnetwork.js b/closure-library/closure/goog/testing/messaging/mockportnetwork.js new file mode 100644 index 0000000000..f116b1c13b --- /dev/null +++ b/closure-library/closure/goog/testing/messaging/mockportnetwork.js @@ -0,0 +1,69 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// 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 fake PortNetwork implementation that simply produces + * MockMessageChannels for all ports. + * + */ + +goog.setTestOnly('goog.testing.messaging.MockPortNetwork'); +goog.provide('goog.testing.messaging.MockPortNetwork'); + +goog.require('goog.messaging.PortNetwork'); // interface +goog.require('goog.testing.messaging.MockMessageChannel'); + +goog.forwardDeclare('goog.testing.MockControl'); + + + +/** + * The fake PortNetwork. + * + * @param {!goog.testing.MockControl} mockControl The mock control for creating + * the mock message channels. + * @constructor + * @implements {goog.messaging.PortNetwork} + * @final + */ +goog.testing.messaging.MockPortNetwork = function(mockControl) { + /** + * The mock control for creating mock message channels. + * @type {!goog.testing.MockControl} + * @private + */ + this.mockControl_ = mockControl; + + /** + * The mock ports that have been created. + * @type {!Object} + * @private + */ + this.ports_ = {}; +}; + + +/** + * Get the mock port with the given name. + * @param {string} name The name of the port to get. + * @return {!goog.testing.messaging.MockMessageChannel} The mock port. + * @override + */ +goog.testing.messaging.MockPortNetwork.prototype.dial = function(name) { + if (!(name in this.ports_)) { + this.ports_[name] = + new goog.testing.messaging.MockMessageChannel(this.mockControl_); + } + return this.ports_[name]; +}; diff --git a/closure-library/closure/goog/testing/mock.js b/closure-library/closure/goog/testing/mock.js new file mode 100644 index 0000000000..d3afeb30d0 --- /dev/null +++ b/closure-library/closure/goog/testing/mock.js @@ -0,0 +1,741 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview This file defines base classes used for creating mocks in + * JavaScript. The API was inspired by EasyMock. + * + * The basic API is: + *

    + *
  • Create an object to be mocked + *
  • Create a mock object, passing in the above object to the constructor + *
  • Set expectations by calling methods on the mock object + *
  • Call $replay() on the mock object + *
  • Pass the mock to code that will make real calls on it + *
  • Call $verify() to make sure that expectations were met + *
+ * + * For examples, please see the unit tests for LooseMock and StrictMock. + * + * Still TODO + * implement better (and pluggable) argument matching + * Have the exceptions for LooseMock show the number of expected/actual calls + * loose and strict mocks share a lot of code - move it to the base class + * + */ + +goog.setTestOnly('goog.testing.Mock'); +goog.provide('goog.testing.Mock'); +goog.provide('goog.testing.MockExpectation'); + +goog.require('goog.Promise'); +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.object'); +goog.require('goog.promise.Resolver'); +goog.require('goog.testing.JsUnitException'); +goog.require('goog.testing.MockInterface'); +goog.require('goog.testing.mockmatchers'); + + + +/** + * This is a class that represents an expectation. + * @param {string} name The name of the method for this expectation. + * @constructor + * @final + */ +goog.testing.MockExpectation = function(name) { + /** + * The name of the method that is expected to be called. + * @type {string} + */ + this.name = name; + + /** + * An array of error messages for expectations not met. + * @type {Array} + */ + this.errorMessages = []; +}; + + +/** + * The minimum number of times this method should be called. + * @type {number} + */ +goog.testing.MockExpectation.prototype.minCalls = 1; + + +/** + * The maximum number of times this method should be called. + * @type {number} + */ +goog.testing.MockExpectation.prototype.maxCalls = 1; + + +/** + * The value that this method should return. + * @type {*} + */ +goog.testing.MockExpectation.prototype.returnValue; + + +/** + * The value that will be thrown when the method is called + * @type {*} + */ +goog.testing.MockExpectation.prototype.exceptionToThrow; + + +/** + * The arguments that are expected to be passed to this function + * @type {Array<*>} + */ +goog.testing.MockExpectation.prototype.argumentList; + + +/** + * The number of times this method is called by real code. + * @type {number} + */ +goog.testing.MockExpectation.prototype.actualCalls = 0; + + +/** + * The number of times this method is called during the verification phase. + * @type {number} + */ +goog.testing.MockExpectation.prototype.verificationCalls = 0; + + +/** + * The function which will be executed when this method is called. + * Method arguments will be passed to this function, and return value + * of this function will be returned by the method. + * @type {Function} + */ +goog.testing.MockExpectation.prototype.toDo; + + +/** + * Allow expectation failures to include messages. + * @param {string} message The failure message. + */ +goog.testing.MockExpectation.prototype.addErrorMessage = function(message) { + this.errorMessages.push(message); +}; + + +/** + * Get the error messages seen so far. + * @return {string} Error messages separated by \n. + */ +goog.testing.MockExpectation.prototype.getErrorMessage = function() { + return this.errorMessages.join('\n'); +}; + + +/** + * Get how many error messages have been seen so far. + * @return {number} Count of error messages. + */ +goog.testing.MockExpectation.prototype.getErrorMessageCount = function() { + return this.errorMessages.length; +}; + + + +/** + * The base class for a mock object. + * @param {Object|Function} objectToMock The object that should be mocked, or + * the constructor of an object to mock. + * @param {boolean=} opt_mockStaticMethods An optional argument denoting that + * a mock should be constructed from the static functions of a class. + * @param {boolean=} opt_createProxy An optional argument denoting that + * a proxy for the target mock should be created. + * @constructor + * @implements {goog.testing.MockInterface} + */ +goog.testing.Mock = function( + objectToMock, opt_mockStaticMethods, opt_createProxy) { + if (!goog.isObject(objectToMock) && !goog.isFunction(objectToMock)) { + throw new Error('objectToMock must be an object or constructor.'); + } + if (opt_createProxy && !opt_mockStaticMethods && + goog.isFunction(objectToMock)) { + /** + * @constructor + * @final + */ + var tempCtor = function() {}; + goog.inherits(tempCtor, objectToMock); + this.$proxy = new tempCtor(); + } else if ( + opt_createProxy && opt_mockStaticMethods && + goog.isFunction(objectToMock)) { + throw new Error('Cannot create a proxy when opt_mockStaticMethods is true'); + } else if (opt_createProxy && !goog.isFunction(objectToMock)) { + throw new Error('Must have a constructor to create a proxy'); + } + + if (goog.isFunction(objectToMock) && !opt_mockStaticMethods) { + this.$initializeFunctions_(objectToMock.prototype); + } else { + this.$initializeFunctions_(objectToMock); + } + this.$argumentListVerifiers_ = {}; + + /** @protected {?goog.promise.Resolver} */ + this.waitingForExpectations = null; +}; + + +/** + * Option that may be passed when constructing function, method, and + * constructor mocks. Indicates that the expected calls should be accepted in + * any order. + * @const + * @type {number} + */ +goog.testing.Mock.LOOSE = 1; + + +/** + * Option that may be passed when constructing function, method, and + * constructor mocks. Indicates that the expected calls should be accepted in + * the recorded order only. + * @const + * @type {number} + */ +goog.testing.Mock.STRICT = 0; + + +/** + * Asserts that a mock object is in record mode. This avoids type system errors + * from mock expectations. + * + * Usage: + * + * ``` + * const record = goog.require('goog.testing.Mock.record'); + * + * record(mockObject).someMethod(ignoreArgument).$returns(42); + * record(mockFunction)(ignoreArgument).$returns(42); + * ``` + * + * @param {?} obj A mock in record mode. + * @return {?} The same object. + */ +goog.testing.Mock.record = function(obj) { + goog.asserts.assert( + obj.$recording_ !== undefined, + obj + ' is not a mock. Did you pass a real object to record()?'); + goog.asserts.assert( + obj.$recording_, + 'Your mock is in replay mode. You can only call record(mock) before mock.$replay()'); + return obj; +}; + + +/** + * This array contains the name of the functions that are part of the base + * Object prototype. + * Basically a copy of goog.object.PROTOTYPE_FIELDS_. + * @const + * @type {!Array} + * @private + */ +goog.testing.Mock.OBJECT_PROTOTYPE_FIELDS_ = [ + 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', + 'toLocaleString', 'toString', 'valueOf' +]; + + +/** + * This array contains the name of the functions that are part of the base + * Function prototype. The restricted field 'caller' and 'arguments' are + * excluded. + * @const + * @type {!Array} + * @private + */ +goog.testing.Mock.FUNCTION_PROTOTYPE_FIELDS_ = ['apply', 'bind', 'call']; + + +/** + * A proxy for the mock. This can be used for dependency injection in lieu of + * the mock if the test requires a strict instanceof check. + * @type {?Object} + */ +goog.testing.Mock.prototype.$proxy = null; + + +/** + * Map of argument name to optional argument list verifier function. + * @type {Object} + */ +goog.testing.Mock.prototype.$argumentListVerifiers_; + + +/** + * Whether or not we are in recording mode. + * @type {boolean} + * @private + */ +goog.testing.Mock.prototype.$recording_ = true; + + +/** + * The expectation currently being created. All methods that modify the + * current expectation return the Mock object for easy chaining, so this is + * where we keep track of the expectation that's currently being modified. + * @type {goog.testing.MockExpectation} + * @protected + */ +goog.testing.Mock.prototype.$pendingExpectation; + + +/** + * First exception thrown by this mock; used in $verify. + * @type {?Object} + * @private + */ +goog.testing.Mock.prototype.$threwException_ = null; + + +/** + * Initializes the functions on the mock object. + * @param {Object} objectToMock The object being mocked. + * @private + */ +goog.testing.Mock.prototype.$initializeFunctions_ = function(objectToMock) { + // Gets the object properties. + var enumerableProperties = goog.object.getAllPropertyNames( + objectToMock, false /* opt_includeObjectPrototype */, + false /* opt_includeFunctionPrototype */); + + if (goog.isFunction(objectToMock)) { + for (var i = 0; i < goog.testing.Mock.FUNCTION_PROTOTYPE_FIELDS_.length; + i++) { + var prop = goog.testing.Mock.FUNCTION_PROTOTYPE_FIELDS_[i]; + // Look at b/6758711 if you're considering adding ALL properties to ALL + // mocks. + if (objectToMock[prop] !== Function.prototype[prop]) { + enumerableProperties.push(prop); + } + } + } + + // The non enumerable properties are added if they override the ones in the + // Object prototype. This is due to the fact that IE8 does not enumerate any + // of the prototype Object functions even when overridden and mocking these is + // sometimes needed. + for (var i = 0; i < goog.testing.Mock.OBJECT_PROTOTYPE_FIELDS_.length; i++) { + var prop = goog.testing.Mock.OBJECT_PROTOTYPE_FIELDS_[i]; + // Look at b/6758711 if you're considering adding ALL properties to ALL + // mocks. + if (objectToMock[prop] !== Object.prototype[prop]) { + enumerableProperties.push(prop); + } + } + + // Adds the properties to the mock. + for (var i = 0; i < enumerableProperties.length; i++) { + var prop = enumerableProperties[i]; + if (typeof objectToMock[prop] == 'function') { + this[prop] = goog.bind(this.$mockMethod, this, prop); + if (this.$proxy) { + this.$proxy[prop] = goog.bind(this.$mockMethod, this, prop); + } + } + } +}; + + +/** + * Registers a verifier function to use when verifying method argument lists. + * @param {string} methodName The name of the method for which the verifierFn + * should be used. + * @param {Function} fn Argument list verifier function. Should take 2 argument + * arrays as arguments, and return true if they are considered equivalent. + * @return {!goog.testing.Mock} This mock object. + */ +goog.testing.Mock.prototype.$registerArgumentListVerifier = function( + methodName, fn) { + this.$argumentListVerifiers_[methodName] = fn; + return this; +}; + + +/** + * The function that replaces all methods on the mock object. + * @param {string} name The name of the method being mocked. + * @return {*} In record mode, returns the mock object. In replay mode, returns + * whatever the creator of the mock set as the return value. + */ +goog.testing.Mock.prototype.$mockMethod = function(name) { + try { + // Shift off the name argument so that args contains the arguments to + // the mocked method. + var args = goog.array.slice(arguments, 1); + if (this.$recording_) { + this.$pendingExpectation = new goog.testing.MockExpectation(name); + this.$pendingExpectation.argumentList = args; + this.$recordExpectation(); + return this; + } else { + return this.$recordCall(name, args); + } + } catch (ex) { + this.$recordAndThrow(ex, true /* rethrow */); + } +}; + + +/** + * Records the currently pending expectation, intended to be overridden by a + * subclass. + * @protected + */ +goog.testing.Mock.prototype.$recordExpectation = function() {}; + + +/** + * Records an actual method call, intended to be overridden by a + * subclass. The subclass must find the pending expectation and return the + * correct value. + * @param {string} name The name of the method being called. + * @param {Array} args The arguments to the method. + * @return {*} The return expected by the mock. + * @protected + */ +goog.testing.Mock.prototype.$recordCall = function(name, args) { + return undefined; +}; + + +/** + * If the expectation expects to throw, this method will throw. + * @param {goog.testing.MockExpectation} expectation The expectation. + */ +goog.testing.Mock.prototype.$maybeThrow = function(expectation) { + if (typeof expectation.exceptionToThrow != 'undefined') { + throw expectation.exceptionToThrow; + } +}; + + +/** + * If this expectation defines a function to be called, + * it will be called and its result will be returned. + * Otherwise, if the expectation expects to throw, it will throw. + * Otherwise, this method will return defined value. + * @param {goog.testing.MockExpectation} expectation The expectation. + * @param {Array} args The arguments to the method. + * @return {*} The return value expected by the mock. + */ +goog.testing.Mock.prototype.$do = function(expectation, args) { + if (typeof expectation.toDo == 'undefined') { + this.$maybeThrow(expectation); + return expectation.returnValue; + } else { + return expectation.toDo.apply(this, args); + } +}; + + +/** + * Specifies a return value for the currently pending expectation. + * @param {*} val The return value. + * @return {!goog.testing.Mock} This mock object. + */ +goog.testing.Mock.prototype.$returns = function(val) { + this.$pendingExpectation.returnValue = val; + return this; +}; + + +/** + * Specifies a value for the currently pending expectation to throw. + * @param {*} val The value to throw. + * @return {!goog.testing.Mock} This mock object. + */ +goog.testing.Mock.prototype.$throws = function(val) { + this.$pendingExpectation.exceptionToThrow = val; + return this; +}; + + +/** + * Specifies a function to call for currently pending expectation. + * Note, that using this method overrides declarations made + * using $returns() and $throws() methods. + * @param {Function} func The function to call. + * @return {!goog.testing.Mock} This mock object. + */ +goog.testing.Mock.prototype.$does = function(func) { + this.$pendingExpectation.toDo = func; + return this; +}; + + +/** + * Allows the expectation to be called 0 or 1 times. + * @return {!goog.testing.Mock} This mock object. + */ +goog.testing.Mock.prototype.$atMostOnce = function() { + this.$pendingExpectation.minCalls = 0; + this.$pendingExpectation.maxCalls = 1; + return this; +}; + + +/** + * Allows the expectation to be called any number of times, as long as it's + * called once. + * @return {!goog.testing.Mock} This mock object. + */ +goog.testing.Mock.prototype.$atLeastOnce = function() { + this.$pendingExpectation.maxCalls = Infinity; + return this; +}; + + +/** + * Allows the expectation to be called exactly once. + * @return {!goog.testing.Mock} This mock object. + */ +goog.testing.Mock.prototype.$once = function() { + this.$pendingExpectation.minCalls = 1; + this.$pendingExpectation.maxCalls = 1; + return this; +}; + + +/** + * Disallows the expectation from being called. + * @return {!goog.testing.Mock} This mock object. + */ +goog.testing.Mock.prototype.$never = function() { + this.$pendingExpectation.minCalls = 0; + this.$pendingExpectation.maxCalls = 0; + return this; +}; + + +/** + * Allows the expectation to be called any number of times. + * @return {!goog.testing.Mock} This mock object. + */ +goog.testing.Mock.prototype.$anyTimes = function() { + this.$pendingExpectation.minCalls = 0; + this.$pendingExpectation.maxCalls = Infinity; + return this; +}; + + +/** + * Specifies the number of times the expectation should be called. + * @param {number} times The number of times this method will be called. + * @return {!goog.testing.Mock} This mock object. + */ +goog.testing.Mock.prototype.$times = function(times) { + this.$pendingExpectation.minCalls = times; + this.$pendingExpectation.maxCalls = times; + return this; +}; + + +/** + * Switches from recording to replay mode. + * @override + */ +goog.testing.Mock.prototype.$replay = function() { + this.$recording_ = false; +}; + + +/** + * Resets the state of this mock object. This clears all pending expectations + * without verifying, and puts the mock in recording mode. + * @override + */ +goog.testing.Mock.prototype.$reset = function() { + this.$recording_ = true; + this.$threwException_ = null; + delete this.$pendingExpectation; + if (this.waitingForExpectations) { + this.waitingForExpectations = null; + } +}; + + +/** + * Throws an exception and records that an exception was thrown. + * @param {string} comment A short comment about the exception. + * @param {?string=} opt_message A longer message about the exception. + * @throws {Object} JsUnitException object. + * @protected + */ +goog.testing.Mock.prototype.$throwException = function(comment, opt_message) { + this.$recordAndThrow(new goog.testing.JsUnitException(comment, opt_message)); +}; + + +/** + * Throws an exception and records that an exception was thrown. + * @param {Object} ex Exception. + * @param {boolean=} rethrow True if this exception has already been thrown. If + * so, we should not report it to TestCase (since it was already reported at + * the original throw). This is necessary to avoid logging it twice, because + * assertThrowsJsUnitException only removes one record. + * @throws {Object} #ex. + * @protected + */ +goog.testing.Mock.prototype.$recordAndThrow = function(ex, rethrow) { + if (this.waitingForExpectations) { + this.waitingForExpectations.resolve(); + } + if (this.$recording_) { + ex = new goog.testing.JsUnitException( + 'Threw an exception while in record mode, did you $replay?', + ex.toString()); + } + // If it's an assert exception, record it. + if (ex['isJsUnitException']) { + if (!this.$threwException_) { + // Only remember first exception thrown. + this.$threwException_ = ex; + } + + // Don't fail if JSUnit isn't loaded. Instead, the test can catch the error + // normally. Other test frameworks won't get automatic failures if assertion + // errors are swallowed. + var getTestCase = + goog.getObjectByName('goog.testing.TestCase.getActiveTestCase'); + var testCase = getTestCase && getTestCase(); + if (testCase && !rethrow) { + testCase.raiseAssertionException(ex); + } + } + throw ex; +}; + + +/** @override */ +goog.testing.Mock.prototype.$waitAndVerify = function() { + goog.asserts.assert( + !this.$recording_, + '$waitAndVerify should be called after recording calls.'); + this.waitingForExpectations = goog.Promise.withResolver(); + var verify = goog.bind(this.$verify, this); + return this.waitingForExpectations.promise.then(function() { + return new goog.Promise(function(resolve, reject) { + setTimeout(function() { + try { + verify(); + } catch (e) { + reject(e); + } + resolve(); + }, 0); + }); + }); +}; + + +/** + * Verify that all of the expectations were met. Should be overridden by + * subclasses. + * @override + */ +goog.testing.Mock.prototype.$verify = function() { + if (this.$threwException_) { + throw this.$threwException_; + } +}; + + +/** + * Verifies that a method call matches an expectation. + * @param {goog.testing.MockExpectation} expectation The expectation to check. + * @param {string} name The name of the called method. + * @param {Array<*>?} args The arguments passed to the mock. + * @return {boolean} Whether the call matches the expectation. + */ +goog.testing.Mock.prototype.$verifyCall = function(expectation, name, args) { + if (expectation.name != name) { + return false; + } + var verifierFn = + this.$argumentListVerifiers_.hasOwnProperty(expectation.name) ? + this.$argumentListVerifiers_[expectation.name] : + goog.testing.mockmatchers.flexibleArrayMatcher; + + return verifierFn(expectation.argumentList, args, expectation); +}; + + +/** + * Render the provided argument array to a string to help + * clients with debugging tests. + * @param {Array<*>?} args The arguments passed to the mock. + * @return {string} Human-readable string. + */ +goog.testing.Mock.prototype.$argumentsAsString = function(args) { + var retVal = []; + for (var i = 0; i < args.length; i++) { + try { + retVal.push(goog.typeOf(args[i])); + } catch (e) { + retVal.push('[unknown]'); + } + } + return '(' + retVal.join(', ') + ')'; +}; + + +/** + * Throw an exception based on an incorrect method call. + * @param {string} name Name of method called. + * @param {Array<*>?} args Arguments passed to the mock. + * @param {goog.testing.MockExpectation=} opt_expectation Expected next call, + * if any. + */ +goog.testing.Mock.prototype.$throwCallException = function( + name, args, opt_expectation) { + var errorStringBuffer = []; + var actualArgsString = this.$argumentsAsString(args); + var expectedArgsString = opt_expectation ? + this.$argumentsAsString(opt_expectation.argumentList) : + ''; + + if (opt_expectation && opt_expectation.name == name) { + errorStringBuffer.push( + 'Bad arguments to ', name, '().\n', 'Actual: ', actualArgsString, '\n', + 'Expected: ', expectedArgsString, '\n', + opt_expectation.getErrorMessage()); + } else { + errorStringBuffer.push( + 'Unexpected call to ', name, actualArgsString, '.', + '\nDid you forget to $replay?'); + if (opt_expectation) { + errorStringBuffer.push( + '\nNext expected call was to ', opt_expectation.name, + expectedArgsString); + } + } + this.$throwException(errorStringBuffer.join('')); +}; diff --git a/closure-library/closure/goog/testing/mockclassfactory.js b/closure-library/closure/goog/testing/mockclassfactory.js new file mode 100644 index 0000000000..bb4a2d6895 --- /dev/null +++ b/closure-library/closure/goog/testing/mockclassfactory.js @@ -0,0 +1,591 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview This file defines a factory that can be used to mock and + * replace an entire class. This allows for mocks to be used effectively with + * "new" instead of having to inject all instances. Essentially, a given class + * is replaced with a proxy to either a loose or strict mock. Proxies locate + * the appropriate mock based on constructor arguments. + * + * The usage is: + *
    + *
  • Create a mock with one of the provided methods with a specifc set of + * constructor arguments + *
  • Set expectations by calling methods on the mock object + *
  • Call $replay() on the mock object + *
  • Instantiate the object as normal + *
  • Call $verify() to make sure that expectations were met + *
  • Call reset on the factory to revert all classes back to their original + * state + *
+ * + * For examples, please see the unit test. + * + */ + + +goog.setTestOnly('goog.testing.MockClassFactory'); +goog.provide('goog.testing.MockClassFactory'); +goog.provide('goog.testing.MockClassRecord'); + +goog.require('goog.array'); +goog.require('goog.object'); +goog.require('goog.testing.LooseMock'); +goog.require('goog.testing.StrictMock'); +goog.require('goog.testing.TestCase'); +goog.require('goog.testing.mockmatchers'); + + + +/** + * A record that represents all the data associated with a mock replacement of + * a given class. + * @param {Object} namespace The namespace in which the mocked class resides. + * @param {string} className The name of the class within the namespace. + * @param {Function} originalClass The original class implementation before it + * was replaced by a proxy. + * @param {Function} proxy The proxy that replaced the original class. + * @constructor + * @final + */ +goog.testing.MockClassRecord = function( + namespace, className, originalClass, proxy) { + /** + * A standard closure namespace (e.g. goog.foo.bar) that contains the mock + * class referenced by this MockClassRecord. + * @type {Object} + * @private + */ + this.namespace_ = namespace; + + /** + * The name of the class within the provided namespace. + * @type {string} + * @private + */ + this.className_ = className; + + /** + * The original class implementation. + * @type {Function} + * @private + */ + this.originalClass_ = originalClass; + + /** + * The proxy being used as a replacement for the original class. + * @type {Function} + * @private + */ + this.proxy_ = proxy; + + /** + * A mocks that will be constructed by their argument list. The entries are + * objects with the format {'args': args, 'mock': mock}. + * @type {!Array<{'args', 'mock'}>} + * @private + */ + this.instancesByArgs_ = []; +}; + + +/** + * A mock associated with the static functions for a given class. + * @type {goog.testing.StrictMock|goog.testing.LooseMock|null} + * @private + */ +goog.testing.MockClassRecord.prototype.staticMock_ = null; + + +/** + * A getter for this record's namespace. + * @return {Object} The namespace. + */ +goog.testing.MockClassRecord.prototype.getNamespace = function() { + return this.namespace_; +}; + + +/** + * A getter for this record's class name. + * @return {string} The name of the class referenced by this record. + */ +goog.testing.MockClassRecord.prototype.getClassName = function() { + return this.className_; +}; + + +/** + * A getter for the original class. + * @return {Function} The original class implementation before mocking. + */ +goog.testing.MockClassRecord.prototype.getOriginalClass = function() { + return this.originalClass_; +}; + + +/** + * A getter for the proxy being used as a replacement for the original class. + * @return {Function} The proxy. + */ +goog.testing.MockClassRecord.prototype.getProxy = function() { + return this.proxy_; +}; + + +/** + * A getter for the static mock. + * @return {goog.testing.StrictMock|goog.testing.LooseMock|null} The static + * mock associated with this record. + */ +goog.testing.MockClassRecord.prototype.getStaticMock = function() { + return this.staticMock_; +}; + + +/** + * A setter for the static mock. + * @param {goog.testing.StrictMock|goog.testing.LooseMock} staticMock A mock to + * associate with the static functions for the referenced class. + */ +goog.testing.MockClassRecord.prototype.setStaticMock = function(staticMock) { + this.staticMock_ = staticMock; +}; + + +/** + * Adds a new mock instance mapping. The mapping connects a set of function + * arguments to a specific mock instance. + * @param {Array} args An array of function arguments. + * @param {goog.testing.StrictMock|goog.testing.LooseMock} mock A mock + * associated with the supplied arguments. + */ +goog.testing.MockClassRecord.prototype.addMockInstance = function(args, mock) { + this.instancesByArgs_.push({args: args, mock: mock}); +}; + + +/** + * Finds the mock corresponding to a given argument set. Throws an error if + * there is no appropriate match found. + * @param {Array} args An array of function arguments. + * @return {goog.testing.StrictMock|goog.testing.LooseMock|null} The mock + * corresponding to a given argument set. + */ +goog.testing.MockClassRecord.prototype.findMockInstance = function(args) { + for (var i = 0; i < this.instancesByArgs_.length; i++) { + var instanceArgs = this.instancesByArgs_[i].args; + if (goog.testing.mockmatchers.flexibleArrayMatcher(instanceArgs, args)) { + return this.instancesByArgs_[i].mock; + } + } + + return null; +}; + + +/** + * Resets this record by reverting all the mocked classes back to the original + * implementation and clearing out the mock instance list. + */ +goog.testing.MockClassRecord.prototype.reset = function() { + this.namespace_[this.className_] = this.originalClass_; + this.instancesByArgs_ = []; +}; + + + +/** + * A factory used to create new mock class instances. It is able to generate + * both static and loose mocks. The MockClassFactory is a singleton since it + * tracks the classes that have been mocked internally. + * @constructor + * @final + */ +goog.testing.MockClassFactory = function() { + if (goog.testing.MockClassFactory.instance_) { + return goog.testing.MockClassFactory.instance_; + } + + /** + * A map from class name -> goog.testing.MockClassRecord. + * @type {Object} + * @private + */ + this.mockClassRecords_ = {}; + + goog.testing.MockClassFactory.instance_ = this; +}; + + +/** + * A singleton instance of the MockClassFactory. + * @type {goog.testing.MockClassFactory?} + * @private + */ +goog.testing.MockClassFactory.instance_ = null; + + +/** + * The names of the fields that are defined on Object.prototype. + * @type {Array} + * @private + */ +goog.testing.MockClassFactory.PROTOTYPE_FIELDS_ = [ + 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', + 'toLocaleString', 'toString', 'valueOf' +]; + + +/** + * Iterates through a namespace to find the name of a given class. This is done + * solely to support compilation since string identifiers would break down. + * Tests usually aren't compiled, but the functionality is supported. + * @param {Object} namespace A javascript namespace (e.g. goog.testing). + * @param {Function} classToMock The class whose name should be returned. + * @return {string} The name of the class. + * @private + */ +goog.testing.MockClassFactory.prototype.getClassName_ = function( + namespace, classToMock) { + var namespaces; + if (namespace === goog.global) { + namespaces = goog.testing.TestCase.getGlobals(); + } else { + namespaces = [namespace]; + } + for (var i = 0; i < namespaces.length; i++) { + for (var prop in namespaces[i]) { + if (namespaces[i][prop] === classToMock) { + return prop; + } + } + } + + throw new Error('Class is not a part of the given namespace'); +}; + + +/** + * Returns whether or not a given class has been mocked. + * @param {string} className The name of the class. + * @return {boolean} Whether or not the given class name has a MockClassRecord. + * @private + */ +goog.testing.MockClassFactory.prototype.classHasMock_ = function(className) { + return !!this.mockClassRecords_[className]; +}; + + +/** + * Returns a proxy constructor closure. Since this is a constructor, "this" + * refers to the local scope of the constructed object thus bind cannot be + * used. + * @param {string} className The name of the class. + * @param {Function} mockFinder A bound function that returns the mock + * associated with a class given the constructor's argument list. + * @return {function(new:?)} A proxy constructor. + * @private + */ +goog.testing.MockClassFactory.prototype.getProxyCtor_ = function( + className, mockFinder) { + return /** @type {function(new:?)} */ (function() { + var self = /** @type {?} */ (this); // unknown this is expected. + self.$mock_ = mockFinder(className, arguments); + if (!self.$mock_) { + // The "arguments" variable is not a proper Array so it must be converted. + var args = Array.prototype.slice.call(arguments, 0); + throw new Error( + 'No mock found for ' + className + ' with arguments ' + + args.join(', ')); + } + }); +}; + + +/** + * Returns a proxy function for a mock class instance. This function cannot + * be used with bind since "this" must refer to the scope of the proxy + * constructor. + * @param {string} fnName The name of the function that should be proxied. + * @return {!Function} A proxy function. + * @private + */ +goog.testing.MockClassFactory.prototype.getProxyFunction_ = function(fnName) { + return /** @type {function(this:?,...?):?} */ (function() { + var self = /** @type {?} */ (this); // unknown this is expected. + return self.$mock_[fnName].apply(self.$mock_, arguments); + }); +}; + + +/** + * Find a mock instance for a given class name and argument list. + * @param {string} className The name of the class. + * @param {Array} args The argument list to match. + * @return {goog.testing.StrictMock|goog.testing.LooseMock} The mock found for + * the given argument list. + * @private + */ +goog.testing.MockClassFactory.prototype.findMockInstance_ = function( + className, args) { + return this.mockClassRecords_[className].findMockInstance(args); +}; + + +/** + * Create a proxy class. A proxy will pass functions to the mock for a class. + * The proxy class only covers prototype methods. A static mock is not build + * simultaneously since it might be strict or loose. The proxy class inherits + * from the target class in order to preserve instanceof checks. + * @param {Object} namespace A javascript namespace (e.g. goog.testing). + * @param {Function} classToMock The class that will be proxied. + * @param {string} className The name of the class. + * @return {!Function} The proxy for provided class. + * @private + * @suppress {missingProperties} Function does not defined base. + */ +goog.testing.MockClassFactory.prototype.createProxy_ = function( + namespace, classToMock, className) { + var proxy = + this.getProxyCtor_(className, goog.bind(this.findMockInstance_, this)); + var protoToProxy = classToMock.prototype; + // Preserve base() call in mocked class + var classToMockBase = classToMock.base; + goog.inherits(proxy, classToMock); + proxy.base = classToMockBase; + + for (var prop in protoToProxy) { + if (goog.isFunction(protoToProxy[prop])) { + proxy.prototype[prop] = this.getProxyFunction_(prop); + } + } + + // For IE the for-in-loop does not contain any properties that are not + // enumerable on the prototype object (for example isPrototypeOf from + // Object.prototype) and it will also not include 'replace' on objects that + // extend String and change 'replace' (not that it is common for anyone to + // extend anything except Object). + // TODO (arv): Implement goog.object.getIterator and replace this loop. + + goog.array.forEach( + goog.testing.MockClassFactory.PROTOTYPE_FIELDS_, function(field) { + if (Object.prototype.hasOwnProperty.call(protoToProxy, field)) { + proxy.prototype[field] = this.getProxyFunction_(field); + } + }, this); + + this.mockClassRecords_[className] = new goog.testing.MockClassRecord( + namespace, className, classToMock, proxy); + namespace[className] = proxy; + return proxy; +}; + + +/** + * Gets either a loose or strict mock for a given class based on a set of + * arguments. + * @param {Object} namespace A javascript namespace (e.g. goog.testing). + * @param {Function} classToMock The class that will be mocked. + * @param {boolean} isStrict Whether or not the mock should be strict. + * @param {IArrayLike} ctorArgs The arguments associated with this + * instance's constructor. + * @return {!goog.testing.StrictMock|!goog.testing.LooseMock} The mock created + * for the provided class. + * @private + */ +goog.testing.MockClassFactory.prototype.getMockClass_ = function( + namespace, classToMock, isStrict, ctorArgs) { + var className = this.getClassName_(namespace, classToMock); + + // The namespace and classToMock variables should be removed from the + // passed in argument stack. + ctorArgs = goog.array.slice(ctorArgs, 2); + + if (goog.isFunction(classToMock)) { + var mock = isStrict ? new goog.testing.StrictMock(classToMock) : + new goog.testing.LooseMock(classToMock); + + if (!this.classHasMock_(className)) { + this.createProxy_(namespace, classToMock, className); + } else { + var instance = this.findMockInstance_(className, ctorArgs); + if (instance) { + throw new Error( + 'Mock instance already created for ' + className + + ' with arguments ' + ctorArgs.join(', ')); + } + } + this.mockClassRecords_[className].addMockInstance(ctorArgs, mock); + + return mock; + } else { + throw new Error( + 'Cannot create a mock class for ' + className + ' of type ' + + typeof classToMock); + } +}; + + +/** + * Gets a strict mock for a given class. + * @param {Object} namespace A javascript namespace (e.g. goog.testing). + * @param {Function} classToMock The class that will be mocked. + * @param {...*} var_args The arguments associated with this instance's + * constructor. + * @return {!goog.testing.StrictMock} The mock created for the provided class. + */ +goog.testing.MockClassFactory.prototype.getStrictMockClass = function( + namespace, classToMock, var_args) { + return /** @type {!goog.testing.StrictMock} */ ( + this.getMockClass_(namespace, classToMock, true, arguments)); +}; + + +/** + * Gets a loose mock for a given class. + * @param {Object} namespace A javascript namespace (e.g. goog.testing). + * @param {Function} classToMock The class that will be mocked. + * @param {...*} var_args The arguments associated with this instance's + * constructor. + * @return {goog.testing.LooseMock} The mock created for the provided class. + */ +goog.testing.MockClassFactory.prototype.getLooseMockClass = function( + namespace, classToMock, var_args) { + return /** @type {goog.testing.LooseMock} */ ( + this.getMockClass_(namespace, classToMock, false, arguments)); +}; + + +/** + * Creates either a loose or strict mock for the static functions of a given + * class. + * @param {Function} classToMock The class whose static functions will be + * mocked. This should be the original class and not the proxy. + * @param {string} className The name of the class. + * @param {Function} proxy The proxy that will replace the original class. + * @param {boolean} isStrict Whether or not the mock should be strict. + * @return {!goog.testing.StrictMock|!goog.testing.LooseMock} The mock created + * for the static functions of the provided class. + * @private + */ +goog.testing.MockClassFactory.prototype.createStaticMock_ = function( + classToMock, className, proxy, isStrict) { + var mock = isStrict ? new goog.testing.StrictMock(classToMock, true) : + new goog.testing.LooseMock(classToMock, false, true); + + for (var prop in classToMock) { + if (goog.isFunction(classToMock[prop])) { + proxy[prop] = goog.bind(mock.$mockMethod, mock, prop); + } else if (classToMock[prop] !== classToMock.prototype) { + proxy[prop] = classToMock[prop]; + } + } + + this.mockClassRecords_[className].setStaticMock(mock); + return mock; +}; + + +/** + * Gets either a loose or strict mock for the static functions of a given class. + * @param {Object} namespace A javascript namespace (e.g. goog.testing). + * @param {Function} classToMock The class whose static functions will be + * mocked. This should be the original class and not the proxy. + * @param {boolean} isStrict Whether or not the mock should be strict. + * @return {goog.testing.StrictMock|goog.testing.LooseMock} The mock created + * for the static functions of the provided class. + * @private + */ +goog.testing.MockClassFactory.prototype.getStaticMock_ = function( + namespace, classToMock, isStrict) { + var className = this.getClassName_(namespace, classToMock); + + if (goog.isFunction(classToMock)) { + if (!this.classHasMock_(className)) { + var proxy = this.createProxy_(namespace, classToMock, className); + var mock = + this.createStaticMock_(classToMock, className, proxy, isStrict); + return mock; + } + + if (!this.mockClassRecords_[className].getStaticMock()) { + var proxy = this.mockClassRecords_[className].getProxy(); + var originalClass = this.mockClassRecords_[className].getOriginalClass(); + var mock = + this.createStaticMock_(originalClass, className, proxy, isStrict); + return mock; + } else { + var mock = this.mockClassRecords_[className].getStaticMock(); + var mockIsStrict = mock instanceof goog.testing.StrictMock; + + if (mockIsStrict != isStrict) { + var mockType = + mock instanceof goog.testing.StrictMock ? 'strict' : 'loose'; + var requestedType = isStrict ? 'strict' : 'loose'; + throw new Error( + 'Requested a ' + requestedType + ' static mock, but a ' + mockType + + ' mock already exists.'); + } + + return mock; + } + } else { + throw new Error( + 'Cannot create a mock for the static functions of ' + className + + ' of type ' + typeof classToMock); + } +}; + + +/** + * Gets a strict mock for the static functions of a given class. + * @param {Object} namespace A javascript namespace (e.g. goog.testing). + * @param {Function} classToMock The class whose static functions will be + * mocked. This should be the original class and not the proxy. + * @return {goog.testing.StrictMock} The mock created for the static functions + * of the provided class. + */ +goog.testing.MockClassFactory.prototype.getStrictStaticMock = function( + namespace, classToMock) { + return /** @type {goog.testing.StrictMock} */ ( + this.getStaticMock_(namespace, classToMock, true)); +}; + + +/** + * Gets a loose mock for the static functions of a given class. + * @param {Object} namespace A javascript namespace (e.g. goog.testing). + * @param {Function} classToMock The class whose static functions will be + * mocked. This should be the original class and not the proxy. + * @return {goog.testing.LooseMock} The mock created for the static functions + * of the provided class. + */ +goog.testing.MockClassFactory.prototype.getLooseStaticMock = function( + namespace, classToMock) { + return /** @type {goog.testing.LooseMock} */ ( + this.getStaticMock_(namespace, classToMock, false)); +}; + + +/** + * Resests the factory by reverting all mocked classes to their original + * implementations and removing all MockClassRecords. + */ +goog.testing.MockClassFactory.prototype.reset = function() { + goog.object.forEach( + this.mockClassRecords_, function(record) { record.reset(); }); + this.mockClassRecords_ = {}; +}; diff --git a/closure-library/closure/goog/testing/mockclock.js b/closure-library/closure/goog/testing/mockclock.js new file mode 100644 index 0000000000..0b0c5be330 --- /dev/null +++ b/closure-library/closure/goog/testing/mockclock.js @@ -0,0 +1,653 @@ +// Copyright 2007 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 Mock Clock implementation for working with setTimeout, + * setInterval, clearTimeout and clearInterval within unit tests. + * + * Derived from jsUnitMockTimeout.js, contributed to JsUnit by + * Pivotal Computer Systems, www.pivotalsf.com + * + */ + +goog.setTestOnly('goog.testing.MockClock'); +goog.provide('goog.testing.MockClock'); + +goog.require('goog.Disposable'); +/** @suppress {extraRequire} */ +goog.require('goog.Promise'); +goog.require('goog.Thenable'); +goog.require('goog.async.run'); +goog.require('goog.testing.PropertyReplacer'); +goog.require('goog.testing.events'); +goog.require('goog.testing.events.Event'); + + + +/** + * Class for unit testing code that uses setTimeout and clearTimeout. + * + * NOTE: If you are using MockClock to test code that makes use of + * goog.fx.Animation, then you must either: + * + * 1. Install and dispose of the MockClock in setUpPage() and tearDownPage() + * respectively (rather than setUp()/tearDown()). + * + * or + * + * 2. Ensure that every test clears the animation queue by calling + * mockClock.tick(x) at the end of each test function (where `x` is large + * enough to complete all animations). + * + * Otherwise, if any animation is left pending at the time that + * MockClock.dispose() is called, that will permanently prevent any future + * animations from playing on the page. + * + * @param {boolean=} opt_autoInstall Install the MockClock at construction time. + * @constructor + * @extends {goog.Disposable} + * @final + */ +goog.testing.MockClock = function(opt_autoInstall) { + goog.Disposable.call(this); + + /** + * Reverse-order queue of timers to fire. + * + * The last item of the queue is popped off. Insertion happens from the + * right. For example, the expiration times for each element of the queue + * might be in the order 300, 200, 200. + * + * @type {?Array} + * @private + */ + this.queue_ = []; + + /** + * Set of timeouts that should be treated as cancelled. + * + * Rather than removing cancelled timers directly from the queue, this set + * simply marks them as deleted so that they can be ignored when their + * turn comes up. The keys are the timeout keys that are cancelled, each + * mapping to true. + * + * @private {Object} + */ + this.deletedKeys_ = {}; + + if (opt_autoInstall) { + this.install(); + } +}; +goog.inherits(goog.testing.MockClock, goog.Disposable); + + +/** + * @typedef {{ + * timeoutKey: number, millis: number, + * runAtMillis: number, funcToCall: Function, recurring: boolean}} + */ +goog.testing.MockClock.QueueObjType_; + +/** + * Default wait timeout for mocking requestAnimationFrame (in milliseconds). + * + * @type {number} + * @const + */ +goog.testing.MockClock.REQUEST_ANIMATION_FRAME_TIMEOUT = 20; + + +/** + * ID to use for next timeout. Timeout IDs must never be reused, even across + * MockClock instances. + * @public {number} + */ +goog.testing.MockClock.nextId = Math.round(Math.random() * 10000); + + +/** + * Count of the number of setTimeout/setInterval/etc. calls received by this + * instance. + * @type {number} + * @private + */ +goog.testing.MockClock.prototype.timeoutsMade_ = 0; + + +/** + * Count of the number of timeout/interval/etc. callbacks triggered by this + * instance. + * @type {number} + * @private + */ +goog.testing.MockClock.prototype.callbacksTriggered_ = 0; + + +/** + * PropertyReplacer instance which overwrites and resets setTimeout, + * setInterval, etc. or null if the MockClock is not installed. + * @type {goog.testing.PropertyReplacer} + * @private + */ +goog.testing.MockClock.prototype.replacer_ = null; + + +/** + * The current simulated time in milliseconds. + * @type {number} + * @private + */ +goog.testing.MockClock.prototype.nowMillis_ = 0; + + +/** + * Additional delay between the time a timeout was set to fire, and the time + * it actually fires. Useful for testing workarounds for this Firefox 2 bug: + * https://bugzilla.mozilla.org/show_bug.cgi?id=291386 + * May be negative. + * @type {number} + * @private + */ +goog.testing.MockClock.prototype.timeoutDelay_ = 0; + + +/** + * The real set timeout for reference. + * @const @private {!Function} + */ +goog.testing.MockClock.REAL_SETTIMEOUT_ = goog.global.setTimeout; + + +/** @type {function():number} */ +goog.testing.MockClock.prototype.oldGoogNow_; + +/** + * Installs the MockClock by overriding the global object's implementation of + * setTimeout, setInterval, clearTimeout and clearInterval. + */ +goog.testing.MockClock.prototype.install = function() { + if (!this.replacer_) { + if (goog.testing.MockClock.REAL_SETTIMEOUT_ !== goog.global.setTimeout) { + if (typeof console !== 'undefined' && console.warn) { + console.warn( + 'Non default setTimeout detected. ' + + 'Use of multiple MockClock instances or other clock mocking ' + + 'should be avoided due to unspecified behavior and ' + + 'the resulting fragility.'); + } + } + + var r = this.replacer_ = new goog.testing.PropertyReplacer(); + r.set(goog.global, 'setTimeout', goog.bind(this.setTimeout_, this)); + r.set(goog.global, 'setInterval', goog.bind(this.setInterval_, this)); + r.set(goog.global, 'setImmediate', goog.bind(this.setImmediate_, this)); + r.set(goog.global, 'clearTimeout', goog.bind(this.clearTimeout_, this)); + r.set(goog.global, 'clearInterval', goog.bind(this.clearInterval_, this)); + // goog.Promise uses goog.async.run. In order to be able to test + // Promise-based code, we need to make sure that goog.async.run uses + // nextTick instead of native browser Promises. This means that it will + // default to setImmediate, which is replaced above. Note that we test for + // the presence of goog.async.run.forceNextTick to be resilient to the case + // where tests replace goog.async.run directly. + goog.async.run.forceNextTick && + goog.async.run.forceNextTick(goog.testing.MockClock.REAL_SETTIMEOUT_); + + // Replace the requestAnimationFrame functions. + this.replaceRequestAnimationFrame_(); + + // PropertyReplacer#set can't be called with renameable functions. + this.oldGoogNow_ = goog.now; + goog.now = goog.bind(this.getCurrentTime, this); + } +}; + + +/** + * Installs the mocks for requestAnimationFrame and cancelRequestAnimationFrame. + * @private + */ +goog.testing.MockClock.prototype.replaceRequestAnimationFrame_ = function() { + var r = this.replacer_; + var requestFuncs = [ + 'requestAnimationFrame', 'webkitRequestAnimationFrame', + 'mozRequestAnimationFrame', 'oRequestAnimationFrame', + 'msRequestAnimationFrame' + ]; + + var cancelFuncs = [ + 'cancelAnimationFrame', 'cancelRequestAnimationFrame', + 'webkitCancelRequestAnimationFrame', 'mozCancelRequestAnimationFrame', + 'oCancelRequestAnimationFrame', 'msCancelRequestAnimationFrame' + ]; + + for (var i = 0; i < requestFuncs.length; ++i) { + if (goog.global && goog.global[requestFuncs[i]]) { + r.set( + goog.global, requestFuncs[i], + goog.bind(this.requestAnimationFrame_, this)); + } + } + + for (var i = 0; i < cancelFuncs.length; ++i) { + if (goog.global && goog.global[cancelFuncs[i]]) { + r.set( + goog.global, cancelFuncs[i], + goog.bind(this.cancelRequestAnimationFrame_, this)); + } + } +}; + + +/** + * Removes the MockClock's hooks into the global object's functions and revert + * to their original values. + */ +goog.testing.MockClock.prototype.uninstall = function() { + if (this.replacer_) { + this.replacer_.reset(); + this.replacer_ = null; + goog.now = this.oldGoogNow_; + } + + this.resetAsyncQueue_(); +}; + + +/** @override */ +goog.testing.MockClock.prototype.disposeInternal = function() { + this.uninstall(); + this.queue_ = null; + this.deletedKeys_ = null; + goog.testing.MockClock.superClass_.disposeInternal.call(this); +}; + + +/** + * Resets the MockClock, removing all timeouts that are scheduled and resets + * the fake timer count. + */ +goog.testing.MockClock.prototype.reset = function() { + this.queue_ = []; + this.deletedKeys_ = {}; + this.nowMillis_ = 0; + this.timeoutsMade_ = 0; + this.callbacksTriggered_ = 0; + this.timeoutDelay_ = 0; + + this.resetAsyncQueue_(); +}; + + +/** + * Resets the async queue when this clock resets. + * @private + */ +goog.testing.MockClock.prototype.resetAsyncQueue_ = function() { + goog.async.run.resetQueue(); +}; + + +/** + * Sets the amount of time between when a timeout is scheduled to fire and when + * it actually fires. + * @param {number} delay The delay in milliseconds. May be negative. + */ +goog.testing.MockClock.prototype.setTimeoutDelay = function(delay) { + this.timeoutDelay_ = delay; +}; + + +/** + * @return {number} delay The amount of time between when a timeout is + * scheduled to fire and when it actually fires, in milliseconds. May + * be negative. + */ +goog.testing.MockClock.prototype.getTimeoutDelay = function() { + return this.timeoutDelay_; +}; + + +/** + * Increments the MockClock's time by a given number of milliseconds, running + * any functions that are now overdue. + * @param {number=} opt_millis Number of milliseconds to increment the counter. + * If not specified, clock ticks 1 millisecond. + * @return {number} Current mock time in milliseconds. + */ +goog.testing.MockClock.prototype.tick = function(opt_millis) { + if (typeof opt_millis != 'number') { + opt_millis = 1; + } + var endTime = this.nowMillis_ + opt_millis; + this.runFunctionsWithinRange_(endTime); + this.nowMillis_ = endTime; + return endTime; +}; + + +/** + * Takes a promise and then ticks the mock clock. If the promise successfully + * resolves, returns the value produced by the promise. If the promise is + * rejected, it throws the rejection as an exception. If the promise is not + * resolved at all, throws an exception. + * Also ticks the general clock by the specified amount. + * Only works with goog.Thenable, hence goog.Promise. Does NOT work with native + * browser promises. + * + * @param {!goog.Thenable} promise A promise that should be resolved after + * the mockClock is ticked for the given opt_millis. + * @param {number=} opt_millis Number of milliseconds to increment the counter. + * If not specified, clock ticks 1 millisecond. + * @return {T} + * @template T + * + * @deprecated Treating Promises as synchronous values is incompatible with + * native promises and async functions. More generally, this code relies on + * promises "pumped" by setTimeout which is not done in production code, + * even for goog.Promise and results unnatural timing between resolved + * promises callback and setTimeout/setInterval callbacks in tests. + */ +goog.testing.MockClock.prototype.tickPromise = function(promise, opt_millis) { + var value; + var error; + var resolved = false; + promise.then( + function(v) { + value = v; + resolved = true; + }, + function(e) { + error = e; + resolved = true; + }); + this.tick(opt_millis); + if (!resolved) { + throw new Error( + 'Promise was expected to be resolved after mock clock tick.'); + } + if (error) { + throw error; + } + return value; +}; + + +/** + * @return {number} The number of timeouts or intervals that have been + * scheduled. A setInterval call is only counted once. + */ +goog.testing.MockClock.prototype.getTimeoutsMade = function() { + return this.timeoutsMade_; +}; + + +/** + * @return {number} The number of timeout or interval callbacks that have been + * triggered. For setInterval, each callback is counted separately. + */ +goog.testing.MockClock.prototype.getCallbacksTriggered = function() { + return this.callbacksTriggered_; +}; + + +/** + * @return {number} The MockClock's current time in milliseconds. + */ +goog.testing.MockClock.prototype.getCurrentTime = function() { + return this.nowMillis_; +}; + + +/** + * @param {number} timeoutKey The timeout key. + * @return {boolean} Whether the timer has been set and not cleared, + * independent of the timeout's expiration. In other words, the timeout + * could have passed or could be scheduled for the future. Either way, + * this function returns true or false depending only on whether the + * provided timeoutKey represents a timeout that has been set and not + * cleared. + */ +goog.testing.MockClock.prototype.isTimeoutSet = function(timeoutKey) { + return timeoutKey < goog.testing.MockClock.nextId && + timeoutKey >= goog.testing.MockClock.nextId - this.timeoutsMade_ && + !this.deletedKeys_[timeoutKey]; +}; + + +/** + * Runs any function that is scheduled before a certain time. Timeouts can + * be made to fire early or late if timeoutDelay_ is non-0. + * @param {number} endTime The latest time in the range, in milliseconds. + * @private + */ +goog.testing.MockClock.prototype.runFunctionsWithinRange_ = function(endTime) { + var adjustedEndTime = endTime - this.timeoutDelay_; + + // Repeatedly pop off the last item since the queue is always sorted. + while (this.queue_ && this.queue_.length && + this.queue_[this.queue_.length - 1].runAtMillis <= adjustedEndTime) { + var timeout = this.queue_.pop(); + + 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. + this.callbacksTriggered_++; + 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); + } + } + } +}; + + +/** + * Schedules a function to be run at a certain time. + * @param {number} timeoutKey The timeout key. + * @param {Function} funcToCall The function to call. + * @param {number} millis The number of milliseconds to call it in. + * @param {boolean} recurring Whether to function call should recur. + * @private + */ +goog.testing.MockClock.prototype.scheduleFunction_ = function( + timeoutKey, funcToCall, millis, recurring) { + if (!goog.isFunction(funcToCall)) { + // Early error for debuggability rather than dying in the next .tick() + throw new TypeError( + 'The provided callback must be a function, not a ' + typeof funcToCall); + } + + var /** !goog.testing.MockClock.QueueObjType_ */ timeout = { + runAtMillis: this.nowMillis_ + millis, + funcToCall: funcToCall, + recurring: recurring, + timeoutKey: timeoutKey, + millis: millis + }; + + goog.testing.MockClock.insert_(timeout, this.queue_); +}; + + +/** + * Inserts a timer descriptor into a descending-order queue. + * + * Later-inserted duplicates appear at lower indices. For example, the + * asterisk in (5,4,*,3,2,1) would be the insertion point for 3. + * + * @param {goog.testing.MockClock.QueueObjType_} timeout The timeout to insert, + * with numerical runAtMillis property. + * @param {Array} queue The queue to + * insert into, with each element having a numerical runAtMillis property. + * @private + */ +goog.testing.MockClock.insert_ = function(timeout, queue) { + // Although insertion of N items is quadratic, requiring goog.structs.Heap + // from a unit test will make tests more prone to breakage. Since unit + // tests are normally small, scalability is not a primary issue. + + // Find an insertion point. Since the queue is in reverse order (so we + // can pop rather than unshift), and later timers with the same time stamp + // should be executed later, we look for the element strictly greater than + // the one we are inserting. + + for (var i = queue.length; i != 0; i--) { + if (queue[i - 1].runAtMillis > timeout.runAtMillis) { + break; + } + queue[i] = queue[i - 1]; + } + + queue[i] = timeout; +}; + + +/** + * Maximum 32-bit signed integer. + * + * Timeouts over this time return immediately in many browsers, due to integer + * overflow. Such known browsers include Firefox, Chrome, and Safari, but not + * IE. + * + * @type {number} + * @private + */ +goog.testing.MockClock.MAX_INT_ = 2147483647; + + +/** + * Schedules a function to be called after `millis` milliseconds. + * Mock implementation for setTimeout. + * @param {Function} funcToCall The function to call. + * @param {number=} opt_millis The number of milliseconds to call it after. + * @return {number} The number of timeouts created. + * @private + */ +goog.testing.MockClock.prototype.setTimeout_ = function( + funcToCall, opt_millis) { + var millis = opt_millis || 0; + if (millis > goog.testing.MockClock.MAX_INT_) { + throw new Error( + 'Bad timeout value: ' + millis + '. Timeouts over MAX_INT ' + + '(24.8 days) cause timeouts to be fired ' + + 'immediately in most browsers, except for IE.'); + } + this.timeoutsMade_++; + this.scheduleFunction_( + goog.testing.MockClock.nextId, funcToCall, millis, false); + return goog.testing.MockClock.nextId++; +}; + + +/** + * Schedules a function to be called every `millis` milliseconds. + * Mock implementation for setInterval. + * @param {Function} funcToCall The function to call. + * @param {number=} opt_millis The number of milliseconds between calls. + * @return {number} The number of timeouts created. + * @private + */ +goog.testing.MockClock.prototype.setInterval_ = function( + funcToCall, opt_millis) { + var millis = opt_millis || 0; + this.timeoutsMade_++; + this.scheduleFunction_( + goog.testing.MockClock.nextId, funcToCall, millis, true); + return goog.testing.MockClock.nextId++; +}; + + +/** + * Schedules a function to be called when an animation frame is triggered. + * Mock implementation for requestAnimationFrame. + * @param {Function} funcToCall The function to call. + * @return {number} The number of timeouts created. + * @private + */ +goog.testing.MockClock.prototype.requestAnimationFrame_ = function(funcToCall) { + return this.setTimeout_(goog.bind(function() { + if (funcToCall) { + funcToCall(this.getCurrentTime()); + } else if (goog.global.mozRequestAnimationFrame) { + var event = new goog.testing.events.Event('MozBeforePaint', goog.global); + event['timeStamp'] = this.getCurrentTime(); + goog.testing.events.fireBrowserEvent(event); + } + }, this), goog.testing.MockClock.REQUEST_ANIMATION_FRAME_TIMEOUT); +}; + + +/** + * Schedules a function to be called immediately after the current JS + * execution. + * Mock implementation for setImmediate. + * @param {Function} funcToCall The function to call. + * @return {number} The number of timeouts created. + * @private + */ +goog.testing.MockClock.prototype.setImmediate_ = function(funcToCall) { + return this.setTimeout_(funcToCall, 0); +}; + + +/** + * Clears a timeout. + * Mock implementation for clearTimeout. + * @param {number} timeoutKey The timeout key to clear. + * @private + */ +goog.testing.MockClock.prototype.clearTimeout_ = function(timeoutKey) { + // Some common libraries register static state with timers. + // This is bad. It leads to all sorts of crazy test problems where + // 1) Test A sets up a new mock clock and a static timer. + // 2) Test B sets up a new mock clock, but re-uses the static timer + // from Test A. + // 3) A timeout key from test A gets cleared, breaking a timeout in + // Test B. + // + // For now, we just hackily fail silently if someone tries to clear a timeout + // key before we've allocated it. + // Ideally, we should throw an exception if we see this happening. + if (this.isTimeoutSet(timeoutKey)) { + this.deletedKeys_[timeoutKey] = true; + } +}; + + +/** + * Clears an interval. + * Mock implementation for clearInterval. + * @param {number} timeoutKey The interval key to clear. + * @private + */ +goog.testing.MockClock.prototype.clearInterval_ = function(timeoutKey) { + this.clearTimeout_(timeoutKey); +}; + + +/** + * Clears a requestAnimationFrame. + * Mock implementation for cancelRequestAnimationFrame. + * @param {number} timeoutKey The requestAnimationFrame key to clear. + * @private + */ +goog.testing.MockClock.prototype.cancelRequestAnimationFrame_ = function( + timeoutKey) { + this.clearTimeout_(timeoutKey); +}; diff --git a/closure-library/closure/goog/testing/mockcontrol.js b/closure-library/closure/goog/testing/mockcontrol.js new file mode 100644 index 0000000000..360933269e --- /dev/null +++ b/closure-library/closure/goog/testing/mockcontrol.js @@ -0,0 +1,234 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A MockControl holds a set of mocks for a particular test. + * It consolidates calls to $replay, $verify, and $tearDown, which simplifies + * the test and helps avoid omissions. + * + * You can create and control a mock: + * var mockFoo = mockControl.addMock(new MyMock(Foo)); + * + * MockControl also exposes some convenience functions that create + * controlled mocks for common mocks: StrictMock, LooseMock, + * FunctionMock, MethodMock, and GlobalFunctionMock. + * + */ + + +goog.setTestOnly('goog.testing.MockControl'); +goog.provide('goog.testing.MockControl'); + +goog.require('goog.Promise'); +goog.require('goog.array'); +goog.require('goog.testing'); +goog.require('goog.testing.LooseMock'); +goog.require('goog.testing.StrictMock'); + + + +/** + * Controls a set of mocks. Controlled mocks are replayed, verified, and + * cleaned-up at the same time. + * @constructor + */ +goog.testing.MockControl = function() { + /** + * The list of mocks being controlled. + * @type {Array} + * @private + */ + this.mocks_ = []; +}; + + +/** + * Takes control of this mock. + * @param {goog.testing.MockInterface} mock Mock to be controlled. + * @return {goog.testing.MockInterface} The same mock passed in, + * for convenience. + */ +goog.testing.MockControl.prototype.addMock = function(mock) { + this.mocks_.push(mock); + return mock; +}; + + +/** + * Calls replay on each controlled mock. + */ +goog.testing.MockControl.prototype.$replayAll = function() { + goog.array.forEach(this.mocks_, function(m) { m.$replay(); }); +}; + + +/** + * Calls reset on each controlled mock. + */ +goog.testing.MockControl.prototype.$resetAll = function() { + goog.array.forEach(this.mocks_, function(m) { m.$reset(); }); +}; + + +/** + * Returns a Promise that resolves when all of the controlled mocks have + * finished and verified. + * @return {!goog.Promise>} + */ +goog.testing.MockControl.prototype.$waitAndVerifyAll = function() { + return goog.Promise.all(goog.array.map(this.mocks_, function(m) { + return m.$waitAndVerify(); + })); +}; + + +/** + * Calls verify on each controlled mock. + */ +goog.testing.MockControl.prototype.$verifyAll = function() { + goog.array.forEach(this.mocks_, function(m) { m.$verify(); }); +}; + + +/** + * Calls tearDown on each controlled mock, if necesssary. + */ +goog.testing.MockControl.prototype.$tearDown = function() { + goog.array.forEach(this.mocks_, function(m) { + if (!m) { + return; + } + + m = /** @type {?} */ (m); + // $tearDown if defined. + if (m.$tearDown) { + m.$tearDown(); + } + // TODO(user): Somehow determine if verifyAll should have been called + // but was not. + }); +}; + + +/** + * Creates a controlled StrictMock. Passes its arguments through to the + * StrictMock constructor. + * @param {Object|Function} objectToMock The object that should be mocked, or + * the constructor of an object to mock. + * @param {boolean=} opt_mockStaticMethods An optional argument denoting that + * a mock should be constructed from the static functions of a class. + * @param {boolean=} opt_createProxy An optional argument denoting that + * a proxy for the target mock should be created. + * @return {!goog.testing.StrictMock} The mock object. + */ +goog.testing.MockControl.prototype.createStrictMock = function( + objectToMock, opt_mockStaticMethods, opt_createProxy) { + var m = new goog.testing.StrictMock( + objectToMock, opt_mockStaticMethods, opt_createProxy); + this.addMock(m); + return m; +}; + + +/** + * Creates a controlled LooseMock. Passes its arguments through to the + * LooseMock constructor. + * @param {Object|Function} objectToMock The object that should be mocked, or + * the constructor of an object to mock. + * @param {boolean=} opt_ignoreUnexpectedCalls Whether to ignore unexpected + * calls. + * @param {boolean=} opt_mockStaticMethods An optional argument denoting that + * a mock should be constructed from the static functions of a class. + * @param {boolean=} opt_createProxy An optional argument denoting that + * a proxy for the target mock should be created. + * @return {!goog.testing.LooseMock} The mock object. + */ +goog.testing.MockControl.prototype.createLooseMock = function( + objectToMock, opt_ignoreUnexpectedCalls, opt_mockStaticMethods, + opt_createProxy) { + var m = new goog.testing.LooseMock( + objectToMock, opt_ignoreUnexpectedCalls, opt_mockStaticMethods, + opt_createProxy); + this.addMock(m); + return m; +}; + + +/** + * Creates a controlled FunctionMock. Passes its arguments through to the + * FunctionMock constructor. + * @param {string=} opt_functionName The optional name of the function to mock + * set to '[anonymous mocked function]' if not passed in. + * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or + * goog.testing.Mock.STRICT. The default is STRICT. + * @return {!goog.testing.MockInterface} The mocked function. + */ +goog.testing.MockControl.prototype.createFunctionMock = function( + opt_functionName, opt_strictness) { + var m = goog.testing.createFunctionMock(opt_functionName, opt_strictness); + this.addMock(m); + return m; +}; + + +/** + * Creates a controlled MethodMock. Passes its arguments through to the + * MethodMock constructor. + * @param {Object} scope The scope of the method to be mocked out. + * @param {string} functionName The name of the function we're going to mock. + * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or + * goog.testing.Mock.STRICT. The default is STRICT. + * @return {!goog.testing.MockInterface} The mocked method. + */ +goog.testing.MockControl.prototype.createMethodMock = function( + scope, functionName, opt_strictness) { + var m = goog.testing.createMethodMock(scope, functionName, opt_strictness); + this.addMock(m); + return m; +}; + + +/** + * Creates a controlled MethodMock for a constructor. Passes its arguments + * through to the MethodMock constructor. See + * {@link goog.testing.createConstructorMock} for details. + * @param {Object} scope The scope of the constructor to be mocked out. + * @param {string} constructorName The name of the function we're going to mock. + * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or + * goog.testing.Mock.STRICT. The default is STRICT. + * @return {!goog.testing.MockInterface} The mocked method. + */ +goog.testing.MockControl.prototype.createConstructorMock = function( + scope, constructorName, opt_strictness) { + var m = goog.testing.createConstructorMock( + scope, constructorName, opt_strictness); + this.addMock(m); + return m; +}; + + +/** + * Creates a controlled GlobalFunctionMock. Passes its arguments through to the + * GlobalFunctionMock constructor. + * @param {string} functionName The name of the function we're going to mock. + * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or + * goog.testing.Mock.STRICT. The default is STRICT. + * @return {!goog.testing.MockInterface} The mocked function. + */ +goog.testing.MockControl.prototype.createGlobalFunctionMock = function( + functionName, opt_strictness) { + var m = goog.testing.createGlobalFunctionMock(functionName, opt_strictness); + this.addMock(m); + return m; +}; diff --git a/closure-library/closure/goog/testing/mockinterface.js b/closure-library/closure/goog/testing/mockinterface.js new file mode 100644 index 0000000000..5f81cb8ef8 --- /dev/null +++ b/closure-library/closure/goog/testing/mockinterface.js @@ -0,0 +1,55 @@ +// Copyright 2010 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview An interface that all mocks should share. + * @author nicksantos@google.com (Nick Santos) + */ + +goog.setTestOnly('goog.testing.MockInterface'); +goog.provide('goog.testing.MockInterface'); + +goog.require('goog.Promise'); + + + +/** @interface */ +goog.testing.MockInterface = function() {}; + + +/** + * Write down all the expected functions that have been called on the + * mock so far. From here on out, future function calls will be + * compared against this list. + */ +goog.testing.MockInterface.prototype.$replay = function() {}; + + +/** + * Reset the mock. + */ +goog.testing.MockInterface.prototype.$reset = function() {}; + + +/** + * Waits for the Mock to gather expectations and then performs verify. + * @return {!goog.Promise} + */ +goog.testing.MockInterface.prototype.$waitAndVerify = function() {}; + + +/** + * Assert that the expected function calls match the actual calls. + */ +goog.testing.MockInterface.prototype.$verify = function() {}; diff --git a/closure-library/closure/goog/testing/mockmatchers.js b/closure-library/closure/goog/testing/mockmatchers.js new file mode 100644 index 0000000000..3fde35d8f1 --- /dev/null +++ b/closure-library/closure/goog/testing/mockmatchers.js @@ -0,0 +1,396 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Matchers to be used with the mock utilities. They allow for + * flexible matching by type. Custom matchers can be created by passing a + * matcher function into an ArgumentMatcher instance. + * + * For examples, please see the unit test. + * + */ + + +goog.setTestOnly('goog.testing.mockmatchers'); +goog.provide('goog.testing.mockmatchers'); +goog.provide('goog.testing.mockmatchers.ArgumentMatcher'); +goog.provide('goog.testing.mockmatchers.IgnoreArgument'); +goog.provide('goog.testing.mockmatchers.InstanceOf'); +goog.provide('goog.testing.mockmatchers.ObjectEquals'); +goog.provide('goog.testing.mockmatchers.RegexpMatch'); +goog.provide('goog.testing.mockmatchers.SaveArgument'); +goog.provide('goog.testing.mockmatchers.TypeOf'); + +goog.require('goog.array'); +goog.require('goog.dom'); +goog.require('goog.testing.asserts'); + +goog.forwardDeclare('goog.testing.MockExpectation'); // circular + + + +/** + * A simple interface for executing argument matching. A match in this case is + * testing to see if a supplied object fits a given criteria. True is returned + * if the given criteria is met. + * @param {Function=} opt_matchFn A function that evaluates a given argument + * and returns true if it meets a given criteria. + * @param {?string=} opt_matchName The name expressing intent as part of + * an error message for when a match fails. + * @constructor + */ +goog.testing.mockmatchers.ArgumentMatcher = function( + opt_matchFn, opt_matchName) { + /** + * A function that evaluates a given argument and returns true if it meets a + * given criteria. + * @type {Function} + * @private + */ + this.matchFn_ = opt_matchFn || null; + + /** + * A string indicating the match intent (e.g. isBoolean or isString). + * @type {?string} + * @private + */ + this.matchName_ = opt_matchName || null; +}; + + +/** + * A function that takes a match argument and an optional MockExpectation + * which (if provided) will get error information and returns whether or + * not it matches. + * @param {*} toVerify The argument that should be verified. + * @param {?goog.testing.MockExpectation=} opt_expectation The expectation + * for this match. + * @return {boolean} Whether or not a given argument passes verification. + */ +goog.testing.mockmatchers.ArgumentMatcher.prototype.matches = function( + toVerify, opt_expectation) { + if (this.matchFn_) { + var isamatch = this.matchFn_(toVerify); + if (!isamatch && opt_expectation) { + if (this.matchName_) { + opt_expectation.addErrorMessage( + 'Expected: ' + this.matchName_ + ' but was: ' + + _displayStringForValue(toVerify)); + } else { + opt_expectation.addErrorMessage( + 'Expected: missing mockmatcher' + + ' description but was: ' + _displayStringForValue(toVerify)); + } + } + return isamatch; + } else { + throw new Error('No match function defined for this mock matcher'); + } +}; + + + +/** + * A matcher that verifies that an argument is an instance of a given class. + * @param {Function} ctor The class that will be used for verification. + * @constructor + * @extends {goog.testing.mockmatchers.ArgumentMatcher} + * @final + */ +goog.testing.mockmatchers.InstanceOf = function(ctor) { + goog.testing.mockmatchers.ArgumentMatcher.call(this, function(obj) { + return obj instanceof ctor; + // NOTE: Browser differences on ctor.toString() output + // make using that here problematic. So for now, just let + // people know the instanceOf() failed without providing + // browser specific details... + }, 'instanceOf()'); +}; +goog.inherits( + goog.testing.mockmatchers.InstanceOf, + goog.testing.mockmatchers.ArgumentMatcher); + + + +/** + * A matcher that verifies that an argument is of a given type (e.g. "object"). + * @param {string} type The type that a given argument must have. + * @constructor + * @extends {goog.testing.mockmatchers.ArgumentMatcher} + * @final + */ +goog.testing.mockmatchers.TypeOf = function(type) { + goog.testing.mockmatchers.ArgumentMatcher.call(this, function(obj) { + return goog.typeOf(obj) == type; + }, 'typeOf(' + type + ')'); +}; +goog.inherits( + goog.testing.mockmatchers.TypeOf, + goog.testing.mockmatchers.ArgumentMatcher); + + + +/** + * A matcher that verifies that an argument matches a given RegExp. + * @param {RegExp} regexp The regular expression that the argument must match. + * @constructor + * @extends {goog.testing.mockmatchers.ArgumentMatcher} + * @final + */ +goog.testing.mockmatchers.RegexpMatch = function(regexp) { + goog.testing.mockmatchers.ArgumentMatcher.call(this, function(str) { + return regexp.test(str); + }, 'match(' + regexp + ')'); +}; +goog.inherits( + goog.testing.mockmatchers.RegexpMatch, + goog.testing.mockmatchers.ArgumentMatcher); + + + +/** + * A matcher that always returns true. It is useful when the user does not care + * for some arguments. + * For example: mockFunction('username', 'password', IgnoreArgument); + * @constructor + * @extends {goog.testing.mockmatchers.ArgumentMatcher} + * @final + */ +goog.testing.mockmatchers.IgnoreArgument = function() { + goog.testing.mockmatchers.ArgumentMatcher.call( + this, function() { return true; }, 'true'); +}; +goog.inherits( + goog.testing.mockmatchers.IgnoreArgument, + goog.testing.mockmatchers.ArgumentMatcher); + + + +/** + * A matcher that verifies that the argument is an object that equals the given + * expected object, using a deep comparison. + * @param {Object} expectedObject An object to match against when + * verifying the argument. + * @constructor + * @extends {goog.testing.mockmatchers.ArgumentMatcher} + */ +goog.testing.mockmatchers.ObjectEquals = function(expectedObject) { + /** @private */ + this.expectedObject_ = expectedObject; +}; +goog.inherits( + goog.testing.mockmatchers.ObjectEquals, + goog.testing.mockmatchers.ArgumentMatcher); + + +/** @override */ +goog.testing.mockmatchers.ObjectEquals.prototype.matches = function( + toVerify, opt_expectation) { + // Override the default matches implementation to provide a custom error + // message to opt_expectation if it exists. + var differences = + goog.testing.asserts.findDifferences(this.expectedObject_, toVerify); + if (differences) { + if (opt_expectation) { + opt_expectation.addErrorMessage('Expected equal objects\n' + differences); + } + return false; + } + return true; +}; + + + +/** + * A matcher that saves the argument that it is verifying so that your unit test + * can perform extra tests with this argument later. For example, if the + * argument is a callback method, the unit test can then later call this + * callback to test the asynchronous portion of the call. + * @param {goog.testing.mockmatchers.ArgumentMatcher|Function=} opt_matcher + * Argument matcher or matching function that will be used to validate the + * argument. By default, argument will always be valid. + * @param {?string=} opt_matchName The name expressing intent as part of + * an error message for when a match fails. + * @constructor + * @extends {goog.testing.mockmatchers.ArgumentMatcher} + * @final + */ +goog.testing.mockmatchers.SaveArgument = function(opt_matcher, opt_matchName) { + goog.testing.mockmatchers.ArgumentMatcher.call( + this, /** @type {Function} */ (opt_matcher), opt_matchName); + + if (opt_matcher instanceof goog.testing.mockmatchers.ArgumentMatcher) { + /** + * Delegate match requests to this matcher. + * @type {goog.testing.mockmatchers.ArgumentMatcher} + * @private + */ + this.delegateMatcher_ = opt_matcher; + } else if (!opt_matcher) { + this.delegateMatcher_ = goog.testing.mockmatchers.ignoreArgument; + } +}; +goog.inherits( + goog.testing.mockmatchers.SaveArgument, + goog.testing.mockmatchers.ArgumentMatcher); + + +/** @override */ +goog.testing.mockmatchers.SaveArgument.prototype.matches = function( + toVerify, opt_expectation) { + this.arg = toVerify; + if (this.delegateMatcher_) { + return this.delegateMatcher_.matches(toVerify, opt_expectation); + } + return goog.testing.mockmatchers.SaveArgument.superClass_.matches.call( + this, toVerify, opt_expectation); +}; + + +/** + * Saved argument that was verified. + * @type {*} + */ +goog.testing.mockmatchers.SaveArgument.prototype.arg; + + +/** + * An instance of the IgnoreArgument matcher. Returns true for all matches. + * @type {!goog.testing.mockmatchers.IgnoreArgument} + */ +goog.testing.mockmatchers.ignoreArgument = + new goog.testing.mockmatchers.IgnoreArgument(); + + +/** + * A matcher that verifies that an argument is an array. + * @type {!goog.testing.mockmatchers.ArgumentMatcher} + */ +goog.testing.mockmatchers.isArray = + new goog.testing.mockmatchers.ArgumentMatcher(goog.isArray, 'isArray'); + + +/** + * A matcher that verifies that an argument is a array-like. A NodeList is an + * example of a collection that is very close to an array. + * @type {!goog.testing.mockmatchers.ArgumentMatcher} + */ +goog.testing.mockmatchers.isArrayLike = + new goog.testing.mockmatchers.ArgumentMatcher( + goog.isArrayLike, 'isArrayLike'); + + +/** + * A matcher that verifies that an argument is a date-like. + * @type {!goog.testing.mockmatchers.ArgumentMatcher} + */ +goog.testing.mockmatchers.isDateLike = + new goog.testing.mockmatchers.ArgumentMatcher( + goog.isDateLike, 'isDateLike'); + + +/** + * A matcher that verifies that an argument is a string. + * @type {!goog.testing.mockmatchers.ArgumentMatcher} + */ +goog.testing.mockmatchers.isString = + new goog.testing.mockmatchers.ArgumentMatcher(goog.isString, 'isString'); + + +/** + * A matcher that verifies that an argument is a boolean. + * @type {!goog.testing.mockmatchers.ArgumentMatcher} + */ +goog.testing.mockmatchers.isBoolean = + new goog.testing.mockmatchers.ArgumentMatcher(goog.isBoolean, 'isBoolean'); + + +/** + * A matcher that verifies that an argument is a number. + * @type {!goog.testing.mockmatchers.ArgumentMatcher} + */ +goog.testing.mockmatchers.isNumber = + new goog.testing.mockmatchers.ArgumentMatcher(goog.isNumber, 'isNumber'); + + +/** + * A matcher that verifies that an argument is a function. + * @type {!goog.testing.mockmatchers.ArgumentMatcher} + */ +goog.testing.mockmatchers.isFunction = + new goog.testing.mockmatchers.ArgumentMatcher( + goog.isFunction, 'isFunction'); + + +/** + * A matcher that verifies that an argument is an object. + * @type {!goog.testing.mockmatchers.ArgumentMatcher} + */ +goog.testing.mockmatchers.isObject = + new goog.testing.mockmatchers.ArgumentMatcher(goog.isObject, 'isObject'); + + +/** + * A matcher that verifies that an argument is like a DOM node. + * @type {!goog.testing.mockmatchers.ArgumentMatcher} + */ +goog.testing.mockmatchers.isNodeLike = + new goog.testing.mockmatchers.ArgumentMatcher( + goog.dom.isNodeLike, 'isNodeLike'); + + +/** + * A function that checks to see if an array matches a given set of + * expectations. The expectations array can be a mix of ArgumentMatcher + * implementations and values. True will be returned if values are identical or + * if a matcher returns a positive result. + * @param {Array} expectedArr An array of expectations which can be either + * values to check for equality or ArgumentMatchers. + * @param {Array} arr The array to match. + * @param {goog.testing.MockExpectation?=} opt_expectation The expectation + * for this match. + * @return {boolean} Whether or not the given array matches the expectations. + */ +goog.testing.mockmatchers.flexibleArrayMatcher = function( + expectedArr, arr, opt_expectation) { + return goog.array.equals(expectedArr, arr, function(a, b) { + var errCount = 0; + if (opt_expectation) { + errCount = opt_expectation.getErrorMessageCount(); + } + var isamatch = a === b || + a instanceof goog.testing.mockmatchers.ArgumentMatcher && + a.matches(b, opt_expectation); + var failureMessage = null; + if (!isamatch) { + failureMessage = goog.testing.asserts.findDifferences(a, b); + isamatch = !failureMessage; + } + if (!isamatch && opt_expectation) { + // If the error count changed, the match sent out an error + // message. If the error count has not changed, then + // we need to send out an error message... + if (errCount == opt_expectation.getErrorMessageCount()) { + // Use the _displayStringForValue() from assert.js + // for consistency... + if (!failureMessage) { + failureMessage = 'Expected: ' + _displayStringForValue(a) + + ' but was: ' + _displayStringForValue(b); + } + opt_expectation.addErrorMessage(failureMessage); + } + } + return isamatch; + }); +}; diff --git a/closure-library/closure/goog/testing/mockrandom.js b/closure-library/closure/goog/testing/mockrandom.js new file mode 100644 index 0000000000..a606a40467 --- /dev/null +++ b/closure-library/closure/goog/testing/mockrandom.js @@ -0,0 +1,155 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview MockRandom provides a mechanism for specifying a stream of + * numbers to expect from calls to Math.random(). + * + */ + +goog.setTestOnly('goog.testing.MockRandom'); +goog.provide('goog.testing.MockRandom'); + +goog.require('goog.Disposable'); + + + +/** + * Class for unit testing code that uses Math.random. + * + * @param {Array} sequence The sequence of numbers to return. This + * object will modify this array. + * @param {boolean=} opt_install Whether to install the MockRandom at + * construction time. + * @extends {goog.Disposable} + * @constructor + * @final + */ +goog.testing.MockRandom = function(sequence, opt_install) { + goog.Disposable.call(this); + + /** + * The sequence of numbers to be returned by calls to random() + * @type {!Array} + * @private + */ + this.sequence_ = sequence || []; + + /** + * The original Math.random function. + * @type {function(): number} + * @private + */ + this.mathRandom_ = Math.random; + + /** + * Whether to throw an exception when Math.random() is called when there is + * nothing left in the sequence. + * @type {boolean} + * @private + */ + this.strictlyFromSequence_ = false; + + if (opt_install) { + this.install(); + } +}; +goog.inherits(goog.testing.MockRandom, goog.Disposable); + + +/** + * Whether this MockRandom has been installed. + * @type {boolean} + * @private + */ +goog.testing.MockRandom.prototype.installed_; + + +/** + * Installs this MockRandom as the system number generator. + */ +goog.testing.MockRandom.prototype.install = function() { + if (!this.installed_) { + Math.random = goog.bind(this.random, this); + this.installed_ = true; + } +}; + + +/** + * @return {number} The next number in the sequence. If there are no more values + * left, this will return a random number, unless + * `this.strictlyFromSequence_` is true, in which case an error will + * be thrown. + */ +goog.testing.MockRandom.prototype.random = function() { + if (this.hasMoreValues()) { + return this.sequence_.shift(); + } + if (this.strictlyFromSequence_) { + throw new Error('No numbers left in sequence.'); + } + return this.mathRandom_(); +}; + + +/** + * @return {boolean} Whether there are more numbers left in the sequence. + */ +goog.testing.MockRandom.prototype.hasMoreValues = function() { + return this.sequence_.length > 0; +}; + + +/** + * Injects new numbers into the beginning of the sequence. + * @param {!Array|number} values Number or array of numbers to inject. + */ +goog.testing.MockRandom.prototype.inject = function(values) { + if (goog.isArray(values)) { + this.sequence_ = values.concat(this.sequence_); + } else { + this.sequence_.splice(0, 0, values); + } +}; + + +/** + * Uninstalls the MockRandom. + */ +goog.testing.MockRandom.prototype.uninstall = function() { + if (this.installed_) { + Math.random = this.mathRandom_; + this.installed_ = false; + } +}; + + +/** @override */ +goog.testing.MockRandom.prototype.disposeInternal = function() { + this.uninstall(); + delete this.sequence_; + delete this.mathRandom_; + goog.testing.MockRandom.superClass_.disposeInternal.call(this); +}; + + +/** + * @param {boolean} strictlyFromSequence Whether to throw an exception when + * Math.random() is called when there is nothing left in the sequence. + */ +goog.testing.MockRandom.prototype.setStrictlyFromSequence = function( + strictlyFromSequence) { + this.strictlyFromSequence_ = strictlyFromSequence; +}; diff --git a/closure-library/closure/goog/testing/mockrange.js b/closure-library/closure/goog/testing/mockrange.js new file mode 100644 index 0000000000..031c866ce6 --- /dev/null +++ b/closure-library/closure/goog/testing/mockrange.js @@ -0,0 +1,68 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview LooseMock of goog.dom.AbstractRange. + * + */ + +goog.setTestOnly('goog.testing.MockRange'); +goog.provide('goog.testing.MockRange'); + +goog.require('goog.dom.AbstractRange'); +goog.require('goog.testing.LooseMock'); + + + +/** + * LooseMock of goog.dom.AbstractRange. Useful because the mock framework cannot + * simply create a mock out of an abstract class, and cannot create a mock out + * of classes that implements __iterator__ because it relies on the default + * behavior of iterating through all of an object's properties. + * @constructor + * @extends {goog.testing.LooseMock} + * @final + */ +goog.testing.MockRange = function() { + goog.testing.LooseMock.call(this, goog.testing.MockRange.ConcreteRange_); +}; +goog.inherits(goog.testing.MockRange, goog.testing.LooseMock); + + +// *** Private helper class ************************************************* // + + + +/** + * Concrete subclass of goog.dom.AbstractRange that simply sets the abstract + * method __iterator__ to undefined so that javascript defaults to iterating + * through all of the object's properties. + * @constructor + * @extends {goog.dom.AbstractRange} + * @private + */ +goog.testing.MockRange.ConcreteRange_ = function() { + goog.dom.AbstractRange.call(this); +}; +goog.inherits(goog.testing.MockRange.ConcreteRange_, goog.dom.AbstractRange); + + +/** + * Undefine the iterator so the mock framework can loop through this class' + * properties. + * @override + */ +goog.testing.MockRange.ConcreteRange_.prototype.__iterator__ = + // This isn't really type-safe. + /** @type {?} */ (undefined); diff --git a/closure-library/closure/goog/testing/mockstorage.js b/closure-library/closure/goog/testing/mockstorage.js new file mode 100644 index 0000000000..87504a89f5 --- /dev/null +++ b/closure-library/closure/goog/testing/mockstorage.js @@ -0,0 +1,109 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Provides a JS storage class implementing the HTML5 Storage + * interface. + */ + + +goog.setTestOnly('goog.testing.MockStorage'); +goog.provide('goog.testing.MockStorage'); + + +goog.require('goog.structs.Map'); + + + +/** + * A JS storage instance, implementing the HTML5 Storage interface. + * See http://www.w3.org/TR/webstorage/ for details. + * + * @constructor + * @implements {Storage} + * @final + */ +goog.testing.MockStorage = function() { + /** + * The underlying storage object. + * @type {goog.structs.Map} + * @private + */ + this.store_ = new goog.structs.Map(); + + /** + * The number of elements in the storage. + * @type {number} + */ + this.length = 0; +}; + + +/** + * Sets an item to the storage. + * @param {string} key Storage key. + * @param {*} value Storage value. Must be convertible to string. + * @override + */ +goog.testing.MockStorage.prototype.setItem = function(key, value) { + this.store_.set(key, String(value)); + this.length = this.store_.getCount(); +}; + + +/** + * Gets an item from the storage. The item returned is the "structured clone" + * of the value from setItem. In practice this means it's the value cast to a + * string. + * @param {string} key Storage key. + * @return {?string} Storage value for key; null if does not exist. + * @override + */ +goog.testing.MockStorage.prototype.getItem = function(key) { + var val = this.store_.get(key); + // Enforce that getItem returns string values. + return (val != null) ? /** @type {string} */ (val) : null; +}; + + +/** + * Removes and item from the storage. + * @param {string} key Storage key. + * @override + */ +goog.testing.MockStorage.prototype.removeItem = function(key) { + this.store_.remove(key); + this.length = this.store_.getCount(); +}; + + +/** + * Clears the storage. + * @override + */ +goog.testing.MockStorage.prototype.clear = function() { + this.store_.clear(); + this.length = 0; +}; + + +/** + * Returns the key at the given index. + * @param {number} index The index for the key. + * @return {?string} Key at the given index, null if not found. + * @override + */ +goog.testing.MockStorage.prototype.key = function(index) { + return this.store_.getKeys()[index] || null; +}; diff --git a/closure-library/closure/goog/testing/mockuseragent.js b/closure-library/closure/goog/testing/mockuseragent.js new file mode 100644 index 0000000000..525cd0cade --- /dev/null +++ b/closure-library/closure/goog/testing/mockuseragent.js @@ -0,0 +1,188 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview MockUserAgent overrides goog.userAgent.getUserAgentString() + * depending on a specified configuration. + * + */ + +goog.setTestOnly('goog.testing.MockUserAgent'); +goog.provide('goog.testing.MockUserAgent'); + +goog.require('goog.Disposable'); +goog.require('goog.labs.userAgent.util'); +goog.require('goog.testing.PropertyReplacer'); +goog.require('goog.userAgent'); + + + +/** + * Class for unit testing code that uses goog.userAgent. + * + * @extends {goog.Disposable} + * @constructor + * @final + */ +goog.testing.MockUserAgent = function() { + goog.Disposable.call(this); + + /** + * Property replacer used to mock out User-Agent functions. + * @type {!goog.testing.PropertyReplacer} + * @private + */ + this.propertyReplacer_ = new goog.testing.PropertyReplacer(); + + /** + * The userAgent string used by goog.userAgent. + * @type {?string} + * @private + */ + this.userAgent_ = goog.userAgent.getUserAgentString(); + + /** + * The navigator object used by goog.userAgent + * @type {?Navigator} + * @private + */ + this.navigator_ = goog.userAgent.getNavigatorTyped(); + + /** + * The documentMode number used by goog.userAgent + * @type {number|undefined} + * @private + */ + this.documentMode_ = goog.userAgent.DOCUMENT_MODE; +}; +goog.inherits(goog.testing.MockUserAgent, goog.Disposable); + + +/** + * Whether this MockUserAgent has been installed. + * @type {boolean} + * @private + */ +goog.testing.MockUserAgent.prototype.installed_; + + +/** + * Installs this MockUserAgent. + */ +goog.testing.MockUserAgent.prototype.install = function() { + if (!this.installed_) { + // Stub out user agent functions. + this.propertyReplacer_.replace( + goog.userAgent, 'getUserAgentString', + goog.bind(this.getUserAgentString, this)); + + this.propertyReplacer_.replace( + goog.labs.userAgent.util, 'getUserAgent', + goog.bind(this.getUserAgentString, this)); + + // Stub out navigator functions. + this.propertyReplacer_.replace( + goog.userAgent, 'getNavigator', goog.bind(this.getNavigator, this)); + + // Stub out navigator functions. + this.propertyReplacer_.replace( + goog.userAgent, 'getNavigatorTyped', + goog.bind(this.getNavigator, this)); + + // Stub out documentMode functions. + this.propertyReplacer_.replace( + goog.userAgent, 'getDocumentMode_', + goog.bind(this.getDocumentMode, this)); + + this.propertyReplacer_.replace( + goog.userAgent, 'DOCUMENT_MODE', this.getDocumentMode()); + + this.installed_ = true; + } +}; + + +/** + * @return {?string} The userAgent set in this class. + */ +goog.testing.MockUserAgent.prototype.getUserAgentString = function() { + return this.userAgent_; +}; + + +/** + * @param {string} userAgent The desired userAgent string to use. + */ +goog.testing.MockUserAgent.prototype.setUserAgentString = function(userAgent) { + this.userAgent_ = userAgent; +}; + + +/** + * @return {?Object} The Navigator set in this class. + */ +goog.testing.MockUserAgent.prototype.getNavigator = function() { + return this.navigator_; +}; + + +/** + * @return {?Navigator} The Navigator set in this class. + */ +goog.testing.MockUserAgent.prototype.getNavigatorTyped = function() { + return this.navigator_; +}; + +/** + * @param {Object} navigator The desired Navigator object to use. + */ +goog.testing.MockUserAgent.prototype.setNavigator = function(navigator) { + this.navigator_ = /** @type {?Navigator} */ (navigator); +}; + +/** + * @return {number|undefined} The documentMode set in this class. + */ +goog.testing.MockUserAgent.prototype.getDocumentMode = function() { + return this.documentMode_; +}; + +/** + * @param {number} documentMode The desired documentMode to use. + */ +goog.testing.MockUserAgent.prototype.setDocumentMode = function(documentMode) { + this.documentMode_ = documentMode; + this.propertyReplacer_.set(goog.userAgent, 'DOCUMENT_MODE', documentMode); +}; + +/** + * Uninstalls the MockUserAgent. + */ +goog.testing.MockUserAgent.prototype.uninstall = function() { + if (this.installed_) { + this.propertyReplacer_.reset(); + this.installed_ = false; + } + +}; + + +/** @override */ +goog.testing.MockUserAgent.prototype.disposeInternal = function() { + this.uninstall(); + delete this.propertyReplacer_; + delete this.navigator_; + delete this.documentMode_; + goog.testing.MockUserAgent.base(this, 'disposeInternal'); +}; diff --git a/closure-library/closure/goog/testing/multitestrunner.js b/closure-library/closure/goog/testing/multitestrunner.js new file mode 100644 index 0000000000..437f3ccf8b --- /dev/null +++ b/closure-library/closure/goog/testing/multitestrunner.js @@ -0,0 +1,1561 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utility for running multiple test files that utilize the same + * interface as goog.testing.TestRunner. Each test is run in series and their + * results aggregated. The main usecase for the MultiTestRunner is to allow + * the testing of all tests in a project locally. + * + */ + +goog.setTestOnly('goog.testing.MultiTestRunner'); +goog.provide('goog.testing.MultiTestRunner'); +goog.provide('goog.testing.MultiTestRunner.TestFrame'); + +goog.require('goog.Timer'); +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.classlist'); +goog.require('goog.events.EventHandler'); +goog.require('goog.functions'); +goog.require('goog.object'); +goog.require('goog.string'); +goog.require('goog.testing.TestCase'); +goog.require('goog.ui.Component'); +goog.require('goog.ui.ServerChart'); +goog.require('goog.ui.TableSorter'); + + + +/** + * A component for running multiple tests within the browser. + * @param {goog.dom.DomHelper=} opt_domHelper A DOM helper. + * @extends {goog.ui.Component} + * @constructor + * @final + */ +goog.testing.MultiTestRunner = function(opt_domHelper) { + goog.ui.Component.call(this, opt_domHelper); + + /** + * Array of tests to execute, when combined with the base path this should be + * a relative path to the test from the page containing the multi testrunner. + * @type {Array} + * @private + */ + this.allTests_ = []; + + /** + * Tests that match the filter function. + * @type {Array} + * @private + */ + this.activeTests_ = []; + + /** + * An event handler for handling events. + * @type {goog.events.EventHandler} + * @private + */ + this.eh_ = new goog.events.EventHandler(this); + + /** + * A table sorter for the stats. + * @type {goog.ui.TableSorter} + * @private + */ + this.tableSorter_ = new goog.ui.TableSorter(this.dom_); + + /** + * Array to hold individual test reports for tests that failed. + * @type {!Array} + * @private + */ + this.failureReports_ = []; + + /** + * Array of test result objects returned from G_testRunner.getTestResults for + * each individual test run. + * @private {!Array>>} + */ + this.allTestResults_ = []; +}; +goog.inherits(goog.testing.MultiTestRunner, goog.ui.Component); + + +/** + * Default maximimum amount of time to spend at each stage of the test. + * @type {number} + */ +goog.testing.MultiTestRunner.DEFAULT_TIMEOUT_MS = 45 * 1000; + + +/** + * Messages corresponding to the numeric states. + * @type {Array} + */ +goog.testing.MultiTestRunner.STATES = [ + 'waiting for test runner', 'initializing tests', 'waiting for tests to finish' +]; + + +/** + * Event type dispatched when tests are completed. + * @const + */ +goog.testing.MultiTestRunner.TESTS_FINISHED = 'testsFinished'; + + +/** + * The test suite's name. + * @type {string} name + * @private + */ +goog.testing.MultiTestRunner.prototype.name_ = ''; + + +/** + * The base path used to resolve files within the allTests_ array. + * @type {string} + * @private + */ +goog.testing.MultiTestRunner.prototype.basePath_ = ''; + + +/** + * A set of tests that have finished. All extant keys map to true. + * @type {Object} + * @private + */ +goog.testing.MultiTestRunner.prototype.finished_ = null; + + +/** + * Whether the report should contain verbose information about the passes. + * @type {boolean} + * @private + */ +goog.testing.MultiTestRunner.prototype.verbosePasses_ = false; + + +/** + * Whether to hide passing tests completely in the report, makes verbosePasses_ + * obsolete. + * @type {boolean} + * @private + */ +goog.testing.MultiTestRunner.prototype.hidePasses_ = false; + + +/** + * Flag used to tell the test runner to stop after the current test. + * @type {boolean} + * @private + */ +goog.testing.MultiTestRunner.prototype.stopped_ = false; + + +/** + * Flag indicating whether the test runner is active. + * @type {boolean} + * @private + */ +goog.testing.MultiTestRunner.prototype.active_ = false; + + +/** + * Index of the next test to run. + * @type {number} + * @private + */ +goog.testing.MultiTestRunner.prototype.startedCount_ = 0; + + +/** + * Count of the results received so far. + * @type {number} + * @private + */ +goog.testing.MultiTestRunner.prototype.resultCount_ = 0; + + +/** + * Number of passes so far. + * @type {number} + * @private + */ +goog.testing.MultiTestRunner.prototype.passes_ = 0; + + +/** + * Timestamp for the current start time. + * @type {number} + * @private + */ +goog.testing.MultiTestRunner.prototype.startTime_ = 0; + + +/** + * Only tests whose paths patch this filter function will be + * executed. + * @type {function(string): boolean} + * @private + */ +goog.testing.MultiTestRunner.prototype.filterFn_ = goog.functions.TRUE; + + +/** + * Number of milliseconds to wait for loading and initialization steps. + * @type {number} + * @private + */ +goog.testing.MultiTestRunner.prototype.timeoutMs_ = + goog.testing.MultiTestRunner.DEFAULT_TIMEOUT_MS; + + +/** + * @typedef {{ + * testFile: string, + * success: ?boolean, + * runTime: number, + * totalTime: number, + * numFilesLoaded: number + * }} + * @private + */ +goog.testing.MultiTestRunner.StatsType_; + + +/** + * An array of objects containing stats about the tests. + * @type {?Array} + * @private + */ +goog.testing.MultiTestRunner.prototype.stats_ = null; + + +/** + * Reference to the start button element. + * @type {HTMLButtonElement} + * @private + */ +goog.testing.MultiTestRunner.prototype.startButtonEl_ = null; + + +/** + * Reference to the stop button element. + * @type {HTMLButtonElement} + * @private + */ +goog.testing.MultiTestRunner.prototype.stopButtonEl_ = null; + + +/** + * Reference to the log element. + * @type {Element} + * @private + */ +goog.testing.MultiTestRunner.prototype.logEl_ = null; + + +/** + * Reference to the report element. + * @type {Element} + * @private + */ +goog.testing.MultiTestRunner.prototype.reportEl_ = null; + + +/** + * Reference to the stats element. + * @type {Element} + * @private + */ +goog.testing.MultiTestRunner.prototype.statsEl_ = null; + + +/** + * Reference to the progress bar's element. + * @type {Element} + * @private + */ +goog.testing.MultiTestRunner.prototype.progressEl_ = null; + + +/** + * Reference to the progress bar's inner row element. + * @type {HTMLTableRowElement} + * @private + */ +goog.testing.MultiTestRunner.prototype.progressRow_ = null; + + +/** + * Reference to the log tab. + * @type {Element} + * @private + */ +goog.testing.MultiTestRunner.prototype.logTabEl_ = null; + + +/** + * Reference to the report tab. + * @type {Element} + * @private + */ +goog.testing.MultiTestRunner.prototype.reportTabEl_ = null; + + +/** + * Reference to the stats tab. + * @type {Element} + * @private + */ +goog.testing.MultiTestRunner.prototype.statsTabEl_ = null; + + +/** + * The number of tests to run at a time. + * @type {number} + * @private + */ +goog.testing.MultiTestRunner.prototype.poolSize_ = 1; + + +/** + * The size of the stats bucket for the number of files loaded histogram. + * @type {number} + * @private + */ +goog.testing.MultiTestRunner.prototype.numFilesStatsBucketSize_ = 20; + + +/** + * The size of the stats bucket in ms for the run time histogram. + * @type {number} + * @private + */ +goog.testing.MultiTestRunner.prototype.runTimeStatsBucketSize_ = 500; + + +/** + * Sets the name for the test suite. + * @param {string} name The suite's name. + * @return {!goog.testing.MultiTestRunner} Instance for chaining. + */ +goog.testing.MultiTestRunner.prototype.setName = function(name) { + this.name_ = name; + return this; +}; + + +/** + * Returns the name for the test suite. + * @return {string} The name for the test suite. + */ +goog.testing.MultiTestRunner.prototype.getName = function() { + return this.name_; +}; + + +/** + * Sets the basepath that tests added using addTests are resolved with. + * @param {string} path The relative basepath. + * @return {!goog.testing.MultiTestRunner} Instance for chaining. + */ +goog.testing.MultiTestRunner.prototype.setBasePath = function(path) { + this.basePath_ = path; + return this; +}; + + +/** + * Returns the basepath that tests added using addTests are resolved with. + * @return {string} The basepath that tests added using addTests are resolved + * with. + */ +goog.testing.MultiTestRunner.prototype.getBasePath = function() { + return this.basePath_; +}; + + +/** + * Sets whether the report should contain verbose information for tests that + * pass. + * @param {boolean} verbose Whether report should be verbose. + * @return {!goog.testing.MultiTestRunner} Instance for chaining. + */ +goog.testing.MultiTestRunner.prototype.setVerbosePasses = function(verbose) { + this.verbosePasses_ = verbose; + return this; +}; + + +/** + * Returns whether the report should contain verbose information for tests that + * pass. + * @return {boolean} Whether the report should contain verbose information for + * tests that pass. + */ +goog.testing.MultiTestRunner.prototype.getVerbosePasses = function() { + return this.verbosePasses_; +}; + + +/** + * Sets whether the report should contain passing tests at all, makes + * setVerbosePasses obsolete. + * @param {boolean} hide Whether report should not contain passing tests. + * @return {!goog.testing.MultiTestRunner} Instance for chaining. + */ +goog.testing.MultiTestRunner.prototype.setHidePasses = function(hide) { + this.hidePasses_ = hide; + return this; +}; + + +/** + * Returns whether the report should contain passing tests at all, makes + * setVerbosePasses obsolete. + * @return {boolean} Whether the report should contain passing tests at all, + * makes setVerbosePasses obsolete. + */ +goog.testing.MultiTestRunner.prototype.getHidePasses = function() { + return this.hidePasses_; +}; + + +/** + * Sets the bucket sizes for the histograms. + * @param {number} f Bucket size for num files loaded histogram. + * @param {number} t Bucket size for run time histogram. + * @return {!goog.testing.MultiTestRunner} Instance for chaining. + */ +goog.testing.MultiTestRunner.prototype.setStatsBucketSizes = function(f, t) { + this.numFilesStatsBucketSize_ = f; + this.runTimeStatsBucketSize_ = t; + return this; +}; + + +/** + * Sets the number of milliseconds to wait for the page to load, initialize and + * run the tests. + * @param {number} timeout Time in milliseconds. + * @return {!goog.testing.MultiTestRunner} Instance for chaining. + */ +goog.testing.MultiTestRunner.prototype.setTimeout = function(timeout) { + this.timeoutMs_ = timeout; + return this; +}; + + +/** + * Returns the number of milliseconds to wait for the page to load, initialize + * and run the tests. + * @return {number} The number of milliseconds to wait for the page to load, + * initialize and run the tests. + */ +goog.testing.MultiTestRunner.prototype.getTimeout = function() { + return this.timeoutMs_; +}; + + +/** + * Sets the number of tests that can be run at the same time. This only improves + * performance due to the amount of time spent loading the tests. + * @param {number} size The number of tests to run at a time. + * @return {!goog.testing.MultiTestRunner} Instance for chaining. + */ +goog.testing.MultiTestRunner.prototype.setPoolSize = function(size) { + this.poolSize_ = size; + return this; +}; + + +/** + * Returns the number of tests that can be run at the same time. This only + * improves performance due to the amount of time spent loading the tests. + * @return {number} The number of tests that can be run at the same time. This + * only improves performance due to the amount of time spent loading the + * tests. + */ +goog.testing.MultiTestRunner.prototype.getPoolSize = function() { + return this.poolSize_; +}; + + +/** + * Sets a filter function. Only test paths that match the filter function + * will be executed. + * @param {function(string): boolean} filterFn Filters test paths. + * @return {!goog.testing.MultiTestRunner} Instance for chaining. + */ +goog.testing.MultiTestRunner.prototype.setFilterFunction = function(filterFn) { + this.filterFn_ = filterFn; + return this; +}; + + +/** + * Returns a filter function. Only test paths that match the filter function + * will be executed. + * @return {function(string): boolean} A filter function. Only test paths that + * match the filter function will be executed. + + */ +goog.testing.MultiTestRunner.prototype.getFilterFunction = function() { + return this.filterFn_; +}; + + +/** + * Adds an array of tests to the tests that the test runner should execute. + * @param {Array} tests Adds tests to the test runner. + * @return {!goog.testing.MultiTestRunner} Instance for chaining. + */ +goog.testing.MultiTestRunner.prototype.addTests = function(tests) { + goog.array.extend(this.allTests_, tests); + return this; +}; + + +/** + * Returns the list of all tests added to the runner. + * @return {Array} The list of all tests added to the runner. + */ +goog.testing.MultiTestRunner.prototype.getAllTests = function() { + return this.allTests_; +}; + + +/** + * Returns the list of tests that will be run when start() is called. + * @return {!Array} The list of tests that will be run when start() is + * called. + */ +goog.testing.MultiTestRunner.prototype.getTestsToRun = function() { + return goog.array.filter(this.allTests_, this.filterFn_); +}; + + +/** + * Returns a list of tests from runner that have been marked as failed. + * @return {!Array} A list of tests from runner that have been marked + * as failed. + */ +goog.testing.MultiTestRunner.prototype.getTestsThatFailed = function() { + var stats = this.stats_; + var failedTests = []; + if (stats) { + for (var i = 0, stat; stat = stats[i]; i++) { + if (!stat.success) { + failedTests.push(stat.testFile); + } + } + } + return failedTests; +}; + + +/** + * Returns a list of reports for tests that have finished since last "start". + * @return {!Array} A list of tests reports. + */ +goog.testing.MultiTestRunner.prototype.getFailureReports = function() { + return this.failureReports_; +}; + + +/** + * Returns list of each frame's test results. + * @return {!Array>>} + */ +goog.testing.MultiTestRunner.prototype.getAllTestResults = function() { + return this.allTestResults_; +}; + + +/** + * Deletes and re-creates the progress table inside the progess element. + * @private + */ +goog.testing.MultiTestRunner.prototype.resetProgressDom_ = function() { + goog.dom.removeChildren(this.progressEl_); + var progressTable = this.dom_.createDom(goog.dom.TagName.TABLE); + var progressTBody = this.dom_.createDom(goog.dom.TagName.TBODY); + this.progressRow_ = this.dom_.createDom(goog.dom.TagName.TR); + for (var i = 0; i < this.activeTests_.length; i++) { + var progressCell = this.dom_.createDom(goog.dom.TagName.TD); + this.progressRow_.appendChild(progressCell); + } + progressTBody.appendChild(this.progressRow_); + progressTable.appendChild(progressTBody); + this.progressEl_.appendChild(progressTable); +}; + + +/** @override */ +goog.testing.MultiTestRunner.prototype.createDom = function() { + goog.testing.MultiTestRunner.superClass_.createDom.call(this); + var el = this.getElement(); + el.className = goog.getCssName('goog-testrunner'); + + this.progressEl_ = this.dom_.createDom(goog.dom.TagName.DIV); + this.progressEl_.className = goog.getCssName('goog-testrunner-progress'); + el.appendChild(this.progressEl_); + + var buttons = this.dom_.createDom(goog.dom.TagName.DIV); + buttons.className = goog.getCssName('goog-testrunner-buttons'); + this.startButtonEl_ = + this.dom_.createDom(goog.dom.TagName.BUTTON, null, 'Start'); + this.stopButtonEl_ = + this.dom_.createDom(goog.dom.TagName.BUTTON, {'disabled': true}, 'Stop'); + buttons.appendChild(this.startButtonEl_); + buttons.appendChild(this.stopButtonEl_); + el.appendChild(buttons); + + this.eh_.listen(this.startButtonEl_, 'click', this.onStartClicked_); + this.eh_.listen(this.stopButtonEl_, 'click', this.onStopClicked_); + + this.logEl_ = this.dom_.createElement(goog.dom.TagName.DIV); + this.logEl_.className = goog.getCssName('goog-testrunner-log'); + el.appendChild(this.logEl_); + + this.reportEl_ = this.dom_.createElement(goog.dom.TagName.DIV); + this.reportEl_.className = goog.getCssName('goog-testrunner-report'); + this.reportEl_.style.display = 'none'; + el.appendChild(this.reportEl_); + + this.statsEl_ = this.dom_.createElement(goog.dom.TagName.DIV); + this.statsEl_.className = goog.getCssName('goog-testrunner-stats'); + this.statsEl_.style.display = 'none'; + el.appendChild(this.statsEl_); + + this.logTabEl_ = this.dom_.createDom(goog.dom.TagName.DIV, null, 'Log'); + this.logTabEl_.className = goog.getCssName('goog-testrunner-logtab') + ' ' + + goog.getCssName('goog-testrunner-activetab'); + el.appendChild(this.logTabEl_); + + this.reportTabEl_ = this.dom_.createDom(goog.dom.TagName.DIV, null, 'Report'); + this.reportTabEl_.className = goog.getCssName('goog-testrunner-reporttab'); + el.appendChild(this.reportTabEl_); + + this.statsTabEl_ = this.dom_.createDom(goog.dom.TagName.DIV, null, 'Stats'); + this.statsTabEl_.className = goog.getCssName('goog-testrunner-statstab'); + el.appendChild(this.statsTabEl_); + + this.eh_.listen(this.logTabEl_, 'click', this.onLogTabClicked_); + this.eh_.listen(this.reportTabEl_, 'click', this.onReportTabClicked_); + this.eh_.listen(this.statsTabEl_, 'click', this.onStatsTabClicked_); + +}; + + +/** @override */ +goog.testing.MultiTestRunner.prototype.disposeInternal = function() { + goog.testing.MultiTestRunner.superClass_.disposeInternal.call(this); + this.tableSorter_.dispose(); + this.eh_.dispose(); + this.startButtonEl_ = null; + this.stopButtonEl_ = null; + this.logEl_ = null; + this.reportEl_ = null; + this.progressEl_ = null; + this.logTabEl_ = null; + this.reportTabEl_ = null; + this.statsTabEl_ = null; + this.statsEl_ = null; +}; + + +/** + * Starts executing the tests. + */ +goog.testing.MultiTestRunner.prototype.start = function() { + this.startButtonEl_.disabled = true; + this.stopButtonEl_.disabled = false; + this.stopped_ = false; + this.active_ = true; + this.finished_ = {}; + this.activeTests_ = this.getTestsToRun(); + this.startedCount_ = 0; + this.resultCount_ = 0; + this.passes_ = 0; + this.stats_ = []; + this.startTime_ = goog.now(); + this.failureReports_ = []; + + this.resetProgressDom_(); + goog.dom.removeChildren(this.logEl_); + + this.resetReport_(); + this.clearStats_(); + this.showTab_(0); + + // No tests to run, finish early and return. + if (this.activeTests_.length == 0) { + this.finish_(); + return; + } + + // Ensure the pool isn't too big. + while (this.getChildCount() > this.poolSize_) { + this.removeChildAt(0, true).dispose(); + } + + // Start a test in each runner. + for (var i = 0; i < this.poolSize_; i++) { + if (i >= this.getChildCount()) { + var testFrame = new goog.testing.MultiTestRunner.TestFrame( + this.basePath_, this.timeoutMs_, this.verbosePasses_, this.dom_); + this.addChild(testFrame, true); + } + this.runNextTest_( + /** @type {goog.testing.MultiTestRunner.TestFrame} */ + (this.getChildAt(i))); + } +}; + + +/** + * Logs a message to the log window. + * @param {string} msg A message to log. + */ +goog.testing.MultiTestRunner.prototype.log = function(msg) { + if (msg != '.') { + msg = this.getTimeStamp_() + ' : ' + msg; + } + + this.logEl_.appendChild(this.dom_.createDom(goog.dom.TagName.DIV, null, msg)); + + // Autoscroll if we're near the bottom. + var top = this.logEl_.scrollTop; + var height = /** @type {!HTMLElement} */ (this.logEl_).scrollHeight - + /** @type {!HTMLElement} */ (this.logEl_).offsetHeight; + if (top == 0 || top > height - 50) { + this.logEl_.scrollTop = height; + } +}; + + +/** + * Processes a result returned from a TestFrame. If there are tests remaining + * it will trigger the next one to be run, otherwise if there are no tests and + * all results have been received then it will call finish. + * @param {goog.testing.MultiTestRunner.TestFrame} frame The frame that just + * finished. + */ +goog.testing.MultiTestRunner.prototype.processResult = function(frame) { + var success = frame.isSuccess(); + var report = frame.getReport(); + var test = frame.getTestFile(); + var stats = frame.getStats(); + + if (!stats.success) { + this.failureReports_.push(report); + } + + this.allTestResults_.push(frame.getTestResults()); + this.stats_.push(/** @type {?} */ (stats)); + this.finished_[test] = true; + + var prefix = success ? '' : '*** FAILURE *** '; + this.log( + prefix + this.trimFileName_(test) + ' : ' + + (success ? 'Passed' : 'Failed')); + + this.resultCount_++; + + if (success) { + this.passes_++; + } + + this.drawProgressSegment_(test, success); + this.writeCurrentSummary_(); + if (!(success && this.hidePasses_)) { + this.drawTestResult_(test, success, report); + } + + if (!this.stopped_ && this.startedCount_ < this.activeTests_.length) { + this.runNextTest_(frame); + } else if (this.resultCount_ == this.activeTests_.length) { + this.finish_(); + } +}; + + +/** + * Runs the next available test, if there are any left. + * @param {goog.testing.MultiTestRunner.TestFrame} frame Where to run the test. + * @private + */ +goog.testing.MultiTestRunner.prototype.runNextTest_ = function(frame) { + if (this.startedCount_ < this.activeTests_.length) { + var nextTest = this.activeTests_[this.startedCount_++]; + this.log(this.trimFileName_(nextTest) + ' : Loading'); + frame.runTest(nextTest); + } +}; + + +/** + * Handles the test finishing, processing the results and rendering the report. + * @private + */ +goog.testing.MultiTestRunner.prototype.finish_ = function() { + if (this.stopped_) { + this.log('Stopped'); + } else { + this.log('Finished'); + } + + this.startButtonEl_.disabled = false; + this.stopButtonEl_.disabled = true; + this.active_ = false; + + this.showTab_(1); + this.drawStats_(); + + // Remove all the test frames + while (this.getChildCount() > 0) { + this.removeChildAt(0, true).dispose(); + } + + // Compute tests that did not finish before the stop button was hit. + var unfinished = []; + for (var i = 0; i < this.activeTests_.length; i++) { + var test = this.activeTests_[i]; + if (!this.finished_[test]) { + unfinished.push(test); + } + } + + if (unfinished.length) { + this.reportEl_.appendChild( + goog.dom.createDom( + goog.dom.TagName.PRE, undefined, + 'These tests did not finish:\n' + unfinished.join('\n'))); + } + + this.dispatchEvent({ + 'type': goog.testing.MultiTestRunner.TESTS_FINISHED, + 'allTestResults': this.getAllTestResults() + }); +}; + + +/** + * Resets the report, clearing out all children and drawing the initial summary. + * @private + */ +goog.testing.MultiTestRunner.prototype.resetReport_ = function() { + goog.dom.removeChildren(this.reportEl_); + var summary = this.dom_.createDom(goog.dom.TagName.DIV); + summary.className = goog.getCssName('goog-testrunner-progress-summary'); + this.reportEl_.appendChild(summary); + this.writeCurrentSummary_(); +}; + + +/** + * Draws the stats for the test run. + * @private + */ +goog.testing.MultiTestRunner.prototype.drawStats_ = function() { + this.drawFilesHistogram_(); + + // Only show time stats if pool size is 1, otherwise times are wrong. + if (this.poolSize_ == 1) { + this.drawRunTimePie_(); + this.drawTimeHistogram_(); + } + + this.drawWorstTestsTable_(); +}; + + +/** + * Draws the histogram showing number of files loaded. + * @private + */ +goog.testing.MultiTestRunner.prototype.drawFilesHistogram_ = function() { + this.drawStatsHistogram_( + 'numFilesLoaded', this.numFilesStatsBucketSize_, goog.functions.identity, + 500, + 'Histogram showing distribution of\nnumber of files loaded per test'); +}; + + +/** + * Draws the histogram showing how long each test took to complete. + * @private + */ +goog.testing.MultiTestRunner.prototype.drawTimeHistogram_ = function() { + this.drawStatsHistogram_( + 'totalTime', this.runTimeStatsBucketSize_, + function(x) { return x / 1000; }, 500, + 'Histogram showing distribution of\ntime spent running tests in s'); +}; + + +/** + * Draws a stats histogram. + * @param {string} statsField Field of the stats object to graph. + * @param {number} bucketSize The size for the histogram's buckets. + * @param {function(number, ...*): *} valueTransformFn Function for + * transforming the x-labels value for display. + * @param {number} width The width in pixels of the graph. + * @param {string} title The graph's title. + * @private + */ +goog.testing.MultiTestRunner.prototype.drawStatsHistogram_ = function( + statsField, bucketSize, valueTransformFn, width, title) { + + var hist = {}, data = [], xlabels = [], ylabels = []; + var max = 0; + for (var i = 0; i < this.stats_.length; i++) { + var num = this.stats_[i][statsField]; + var bucket = Math.floor(num / bucketSize) * bucketSize; + if (bucket > max) { + max = bucket; + } + if (!hist[bucket]) { + hist[bucket] = 1; + } else { + hist[bucket]++; + } + } + var maxBucketSize = 0; + for (var i = 0; i <= max; i += bucketSize) { + xlabels.push(valueTransformFn(i)); + var count = hist[i] || 0; + if (count > maxBucketSize) { + maxBucketSize = count; + } + data.push(count); + } + var diff = Math.max(1, Math.ceil(maxBucketSize / 10)); + for (var i = 0; i <= maxBucketSize; i += diff) { + ylabels.push(i); + } + var chart = new goog.ui.ServerChart( + goog.ui.ServerChart.ChartType.VERTICAL_STACKED_BAR, width, 250, null, + goog.ui.ServerChart.CHART_SERVER_HTTPS_URI); + chart.setTitle(title); + chart.addDataSet(data, 'ff9900'); + chart.setLeftLabels(ylabels); + chart.setGridY(ylabels.length - 1); + chart.setXLabels(xlabels); + chart.render(this.statsEl_); +}; + + +/** + * Draws a pie chart showing the percentage of time spent running the tests + * compared to loading them etc. + * @private + */ +goog.testing.MultiTestRunner.prototype.drawRunTimePie_ = function() { + var totalTime = 0, runTime = 0; + for (var i = 0; i < this.stats_.length; i++) { + var stat = this.stats_[i]; + totalTime += stat.totalTime; + runTime += stat.runTime; + } + var loadTime = totalTime - runTime; + var pie = new goog.ui.ServerChart( + goog.ui.ServerChart.ChartType.PIE, 500, 250, null, + goog.ui.ServerChart.CHART_SERVER_HTTPS_URI); + pie.setMinValue(0); + pie.setMaxValue(totalTime); + pie.addDataSet([runTime, loadTime], 'ff9900'); + pie.setXLabels( + ['Test execution (' + runTime + 'ms)', 'Loading (' + loadTime + 'ms)']); + pie.render(this.statsEl_); +}; + + +/** + * Draws a pie chart showing the percentage of time spent running the tests + * compared to loading them etc. + * @private + */ +goog.testing.MultiTestRunner.prototype.drawWorstTestsTable_ = function() { + this.stats_.sort(function(a, b) { + return b['numFilesLoaded'] - a['numFilesLoaded']; + }); + + var tbody = goog.bind(this.dom_.createDom, this.dom_, 'tbody'); + var thead = goog.bind(this.dom_.createDom, this.dom_, 'thead'); + var tr = goog.bind(this.dom_.createDom, this.dom_, 'tr'); + var th = goog.bind(this.dom_.createDom, this.dom_, 'th'); + var td = goog.bind(this.dom_.createDom, this.dom_, 'td'); + var a = goog.bind(this.dom_.createDom, this.dom_, 'a'); + + var head = thead( + {'style': 'cursor: pointer'}, + tr(null, th(null, ' '), th(null, 'Test file'), + th('center', 'Num files loaded'), th('center', 'Run time (ms)'), + th('center', 'Total time (ms)'))); + var body = tbody(); + var table = this.dom_.createDom(goog.dom.TagName.TABLE, null, head, body); + + for (var i = 0; i < this.stats_.length; i++) { + var stat = this.stats_[i]; + body.appendChild( + tr(null, td('center', String(i + 1)), + td(null, + a({'href': this.basePath_ + stat['testFile'], 'target': '_blank'}, + stat['testFile'])), + td('center', String(stat['numFilesLoaded'])), + td('center', String(stat['runTime'])), + td('center', String(stat['totalTime'])))); + } + + this.statsEl_.appendChild(table); + + this.tableSorter_.setDefaultSortFunction(goog.ui.TableSorter.numericSort); + this.tableSorter_.setSortFunction( + 1 /* test file name */, goog.ui.TableSorter.alphaSort); + this.tableSorter_.decorate(table); +}; + + +/** + * Clears the stats page. + * @private + */ +goog.testing.MultiTestRunner.prototype.clearStats_ = function() { + goog.dom.removeChildren(this.statsEl_); + this.tableSorter_.exitDocument(); +}; + + +/** + * Updates the report's summary. + * @private + */ +goog.testing.MultiTestRunner.prototype.writeCurrentSummary_ = function() { + var total = this.activeTests_.length; + var executed = this.resultCount_; + var passes = this.passes_; + var duration = Math.round((goog.now() - this.startTime_) / 1000); + var text = executed + ' of ' + total + ' tests executed.
' + passes + + ' passed, ' + (executed - passes) + ' failed.
' + + 'Duration: ' + duration + 's.'; + goog.dom.getFirstElementChild(this.reportEl_).innerHTML = text; +}; + + +/** + * Adds a segment to the progress bar. + * @param {string} title Title for the segment. + * @param {*} success Whether the segment should indicate a success. + * @private + */ +goog.testing.MultiTestRunner.prototype.drawProgressSegment_ = function( + title, success) { + var part = this.progressRow_.cells[this.resultCount_ - 1]; + part.title = title + ' : ' + (success ? 'SUCCESS' : 'FAILURE'); + part.style.backgroundColor = success ? '#090' : '#900'; +}; + + +/** + * Draws a test result in the report pane. + * @param {string} test Test name. + * @param {*} success Whether the test succeeded. + * @param {string} report The report. + * @private + */ +goog.testing.MultiTestRunner.prototype.drawTestResult_ = function( + test, success, report) { + var text = goog.string.isEmptyOrWhitespace(report) ? + 'No report for ' + test + '\n' : + report; + var el = this.dom_.createDom(goog.dom.TagName.DIV); + text = goog.string.htmlEscape(text).replace(/\n/g, '
'); + if (success) { + el.className = goog.getCssName('goog-testrunner-report-success'); + } else { + text += '
Run individually »
 '; + el.className = goog.getCssName('goog-testrunner-report-failure'); + } + el.innerHTML = text; + this.reportEl_.appendChild(el); +}; + + +/** + * Returns the current timestamp. + * @return {string} HH:MM:SS. + * @private + */ +goog.testing.MultiTestRunner.prototype.getTimeStamp_ = function() { + var d = new Date; + return goog.string.padNumber(d.getHours(), 2) + ':' + + goog.string.padNumber(d.getMinutes(), 2) + ':' + + goog.string.padNumber(d.getSeconds(), 2); +}; + + +/** + * Trims a filename to be less than 35-characters, ensuring that we do not break + * a path part. + * @param {string} name The file name. + * @return {string} The shortened name. + * @private + */ +goog.testing.MultiTestRunner.prototype.trimFileName_ = function(name) { + if (name.length < 35) { + return name; + } + var parts = name.split('/'); + var result = ''; + while (result.length < 35 && parts.length > 0) { + result = '/' + parts.pop() + result; + } + return '...' + result; +}; + + +/** + * Shows the report and hides the log if the argument is true. + * @param {number} tab Which tab to show. + * @private + */ +goog.testing.MultiTestRunner.prototype.showTab_ = function(tab) { + var activeTabCssClass = goog.getCssName('goog-testrunner-activetab'); + + var logTabElement = goog.asserts.assert(this.logTabEl_); + var reportTabElement = goog.asserts.assert(this.reportTabEl_); + var statsTabElement = goog.asserts.assert(this.statsTabEl_); + + if (tab == 0) { + this.logEl_.style.display = ''; + goog.dom.classlist.add(logTabElement, activeTabCssClass); + } else { + this.logEl_.style.display = 'none'; + goog.dom.classlist.remove(logTabElement, activeTabCssClass); + } + + if (tab == 1) { + this.reportEl_.style.display = ''; + goog.dom.classlist.add(reportTabElement, activeTabCssClass); + } else { + this.reportEl_.style.display = 'none'; + goog.dom.classlist.remove(reportTabElement, activeTabCssClass); + } + + if (tab == 2) { + this.statsEl_.style.display = ''; + goog.dom.classlist.add(statsTabElement, activeTabCssClass); + } else { + this.statsEl_.style.display = 'none'; + goog.dom.classlist.remove(statsTabElement, activeTabCssClass); + } +}; + + +/** + * Handles the start button being clicked. + * @param {goog.events.BrowserEvent} e The click event. + * @private + */ +goog.testing.MultiTestRunner.prototype.onStartClicked_ = function(e) { + this.start(); +}; + + +/** + * Handles the stop button being clicked. + * @param {goog.events.BrowserEvent} e The click event. + * @private + */ +goog.testing.MultiTestRunner.prototype.onStopClicked_ = function(e) { + this.stopped_ = true; + this.finish_(); +}; + + +/** + * Handles the log tab being clicked. + * @param {goog.events.BrowserEvent} e The click event. + * @private + */ +goog.testing.MultiTestRunner.prototype.onLogTabClicked_ = function(e) { + this.showTab_(0); +}; + + +/** + * Handles the log tab being clicked. + * @param {goog.events.BrowserEvent} e The click event. + * @private + */ +goog.testing.MultiTestRunner.prototype.onReportTabClicked_ = function(e) { + this.showTab_(1); +}; + + +/** + * Handles the stats tab being clicked. + * @param {goog.events.BrowserEvent} e The click event. + * @private + */ +goog.testing.MultiTestRunner.prototype.onStatsTabClicked_ = function(e) { + this.showTab_(2); +}; + + + +/** + * Class used to manage the interaction with a single iframe. + * @param {string} basePath The base path for tests. + * @param {number} timeoutMs The time to wait for the test to load and run. + * @param {boolean} verbosePasses Whether to show results for passes. + * @param {goog.dom.DomHelper=} opt_domHelper Optional dom helper. + * @constructor + * @extends {goog.ui.Component} + * @final + */ +goog.testing.MultiTestRunner.TestFrame = function( + basePath, timeoutMs, verbosePasses, opt_domHelper) { + goog.ui.Component.call(this, opt_domHelper); + + /** + * Base path where tests should be resolved from. + * @type {string} + * @private + */ + this.basePath_ = basePath; + + /** + * The timeout for the test. + * @type {number} + * @private + */ + this.timeoutMs_ = timeoutMs; + + /** + * Whether to show a summary for passing tests. + * @type {boolean} + * @private + */ + this.verbosePasses_ = verbosePasses; + + /** + * An event handler for handling events. + * @type {goog.events.EventHandler} + * @private + */ + this.eh_ = new goog.events.EventHandler(this); + + /** + * Object to hold test results. Key is test method or file name (depending on + * failure mode) and the value is an array of failure messages. + * @private {!Object>} + */ + this.testResults_ = {}; +}; +goog.inherits(goog.testing.MultiTestRunner.TestFrame, goog.ui.Component); + + +/** + * Reference to the iframe. + * @type {HTMLIFrameElement} + * @private + */ +goog.testing.MultiTestRunner.TestFrame.prototype.iframeEl_ = null; + + +/** + * Whether the iframe for the current test has loaded. + * @type {boolean} + * @private + */ +goog.testing.MultiTestRunner.TestFrame.prototype.iframeLoaded_ = false; + + +/** + * The test file being run. + * @type {string} + * @private + */ +goog.testing.MultiTestRunner.TestFrame.prototype.testFile_ = ''; + + +/** + * The report returned from the test. + * @type {string} + * @private + */ +goog.testing.MultiTestRunner.TestFrame.prototype.report_ = ''; + + +/** + * The total time loading and running the test in milliseconds. + * @type {number} + * @private + */ +goog.testing.MultiTestRunner.TestFrame.prototype.totalTime_ = 0; + + +/** + * The actual runtime of the test in milliseconds. + * @type {number} + * @private + */ +goog.testing.MultiTestRunner.TestFrame.prototype.runTime_ = 0; + + +/** + * The number of files loaded by the test. + * @type {number} + * @private + */ +goog.testing.MultiTestRunner.TestFrame.prototype.numFilesLoaded_ = 0; + + +/** + * Whether the test was successful, null if no result has been returned yet. + * @type {?boolean} + * @private + */ +goog.testing.MultiTestRunner.TestFrame.prototype.isSuccess_ = null; + + +/** + * Timestamp for the when the test was started. + * @type {number} + * @private + */ +goog.testing.MultiTestRunner.TestFrame.prototype.startTime_ = 0; + + +/** + * Timestamp for the last state, used to determine timeouts. + * @type {number} + * @private + */ +goog.testing.MultiTestRunner.TestFrame.prototype.lastStateTime_ = 0; + + +/** + * The state of the active test. + * @type {number} + * @private + */ +goog.testing.MultiTestRunner.TestFrame.prototype.currentState_ = 0; + + +/** @override */ +goog.testing.MultiTestRunner.TestFrame.prototype.disposeInternal = function() { + goog.testing.MultiTestRunner.TestFrame.superClass_.disposeInternal.call(this); + this.dom_.removeNode(this.iframeEl_); + this.eh_.dispose(); + this.iframeEl_ = null; +}; + + +/** + * Runs a test file in this test frame. + * @param {string} testFile The test to run. + */ +goog.testing.MultiTestRunner.TestFrame.prototype.runTest = function(testFile) { + this.lastStateTime_ = this.startTime_ = goog.now(); + + if (!this.iframeEl_) { + this.createIframe_(); + } + + this.iframeLoaded_ = false; + this.currentState_ = 0; + this.isSuccess_ = null; + this.report_ = ''; + this.testResults_ = {}; + this.testFile_ = testFile; + + try { + this.iframeEl_.src = this.basePath_ + testFile; + } catch (e) { + // Failures will trigger a JS exception on the local file system. + this.report_ = this.testFile_ + ' failed to load : ' + e.message; + this.isSuccess_ = false; + this.finish_(); + return; + } + + this.checkForCompletion_(); +}; + + +/** + * @return {string} The test file the TestFrame is running. + */ +goog.testing.MultiTestRunner.TestFrame.prototype.getTestFile = function() { + return this.testFile_; +}; + + +/** + * @return {!goog.testing.MultiTestRunner.StatsType_} Stats about the test run. + */ +goog.testing.MultiTestRunner.TestFrame.prototype.getStats = function() { + return { + 'testFile': this.testFile_, + 'success': this.isSuccess_, + 'runTime': this.runTime_, + 'totalTime': this.totalTime_, + 'numFilesLoaded': this.numFilesLoaded_ + }; +}; + + +/** + * @return {string} The report for the test run. + */ +goog.testing.MultiTestRunner.TestFrame.prototype.getReport = function() { + return this.report_; +}; + + +/** + * @return {!Object>} The results + * per individual test in the file. Key is the test filename concatenated + * with the test name, and the array holds failures. + */ +goog.testing.MultiTestRunner.TestFrame.prototype.getTestResults = function() { + var results = {}; + for (var testName in this.testResults_) { + var testKey = this.testFile_.replace(/\.html$/, ''); + // Concatenate with ":" unless the testName is equivalent to + // testFile_, which means the test timed out or had no test methods and + // there's no way to get the test method name. + if (testName != this.testFile_) { + testKey += ':' + testName; + } + results[testKey] = this.testResults_[testName]; + } + return results; +}; + + +/** + * @return {?boolean} Whether the test frame had a success. + */ +goog.testing.MultiTestRunner.TestFrame.prototype.isSuccess = function() { + return this.isSuccess_; +}; + + +/** + * Handles the TestFrame finishing a single test. + * @private + */ +goog.testing.MultiTestRunner.TestFrame.prototype.finish_ = function() { + this.totalTime_ = goog.now() - this.startTime_; + var parent = this.getParent(); + if (parent instanceof goog.testing.MultiTestRunner) { + parent.processResult(this); + } +}; + + +/** + * Creates an iframe to run the tests in. For overriding in unit tests. + * @private + */ +goog.testing.MultiTestRunner.TestFrame.prototype.createIframe_ = function() { + this.iframeEl_ = this.dom_.createDom(goog.dom.TagName.IFRAME); + this.getElement().appendChild(this.iframeEl_); + this.eh_.listen(this.iframeEl_, 'load', this.onIframeLoaded_); +}; + + +/** + * Handles the iframe loading. + * @param {goog.events.BrowserEvent} e The load event. + * @private + */ +goog.testing.MultiTestRunner.TestFrame.prototype.onIframeLoaded_ = function(e) { + this.iframeLoaded_ = true; +}; + + +/** + * Checks the active test for completion, keeping track of the tests' various + * execution stages. + * @private + */ +goog.testing.MultiTestRunner.TestFrame.prototype.checkForCompletion_ = + function() { + var js = goog.dom.getFrameContentWindow(this.iframeEl_); + switch (this.currentState_) { + case 0: + if (this.iframeLoaded_ && js['G_testRunner']) { + this.lastStateTime_ = goog.now(); + this.currentState_++; + } + break; + case 1: + if (js['G_testRunner']['isInitialized']()) { + this.lastStateTime_ = goog.now(); + this.currentState_++; + } + break; + case 2: + if (js['G_testRunner']['isFinished']()) { + var tr = js['G_testRunner']; + this.isSuccess_ = tr['isSuccess'](); + this.report_ = tr['getReport'](this.verbosePasses_); + this.testResults_ = tr['getTestResults'](); + // If there is a syntax error, or no tests, it's not possible to get the + // individual test method results from TestCase. So just create one here + // based on the test report and filename. + if (goog.object.isEmpty(this.testResults_)) { + // Existence of a report is a signal of a test failure by the test + // runner. + this.testResults_[this.testFile_] = this.isSuccess_ ? [] : [{ + 'message': this.report_, + 'source': this.testFile_, + 'stacktrace': '' + }]; + } + this.runTime_ = tr['getRunTime'](); + this.numFilesLoaded_ = tr['getNumFilesLoaded'](); + this.finish_(); + return; + } + } + + // Check to see if the test has timed out. + if (goog.now() - this.lastStateTime_ > this.timeoutMs_) { + this.report_ = this.testFile_ + ' timed out ' + + goog.testing.MultiTestRunner.STATES[this.currentState_]; + this.testResults_[this.testFile_] = + [{'message': this.report_, 'source': this.testFile_, 'stacktrace': ''}]; + this.isSuccess_ = false; + this.finish_(); + return; + } + + // Check again in 100ms. + goog.Timer.callOnce(this.checkForCompletion_, 100, this); +}; diff --git a/closure-library/closure/goog/testing/net/mockiframeio.js b/closure-library/closure/goog/testing/net/mockiframeio.js new file mode 100644 index 0000000000..de78b33a8c --- /dev/null +++ b/closure-library/closure/goog/testing/net/mockiframeio.js @@ -0,0 +1,310 @@ +// Copyright 2007 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 Mock of IframeIo for unit testing. + */ + +goog.provide('goog.testing.net.MockIFrameIo'); +goog.require('goog.events.EventTarget'); +goog.require('goog.net.ErrorCode'); +goog.require('goog.net.EventType'); +goog.require('goog.net.IframeIo'); +goog.require('goog.testing.TestQueue'); + + + +/** + * Mock implementation of goog.net.IframeIo. This doesn't provide a mock + * implementation for all cases, but it's not too hard to add them as needed. + * @param {goog.testing.TestQueue} testQueue Test queue for inserting test + * events. + * @constructor + * @extends {goog.events.EventTarget} + * @final + */ +goog.testing.net.MockIFrameIo = function(testQueue) { + goog.events.EventTarget.call(this); + + /** + * Queue of events write to + * @type {goog.testing.TestQueue} + * @private + */ + this.testQueue_ = testQueue; + +}; +goog.inherits(goog.testing.net.MockIFrameIo, goog.events.EventTarget); + + +/** + * Whether MockIFrameIo is active. + * @type {boolean} + * @private + */ +goog.testing.net.MockIFrameIo.prototype.active_ = false; + + +/** + * Last content. + * @type {string} + * @private + */ +goog.testing.net.MockIFrameIo.prototype.lastContent_ = ''; + + +/** + * Last error code. + * @type {goog.net.ErrorCode} + * @private + */ +goog.testing.net.MockIFrameIo.prototype.lastErrorCode_ = + goog.net.ErrorCode.NO_ERROR; + + +/** + * Last error message. + * @type {string} + * @private + */ +goog.testing.net.MockIFrameIo.prototype.lastError_ = ''; + + +/** + * Last custom error. + * @type {Object} + * @private + */ +goog.testing.net.MockIFrameIo.prototype.lastCustomError_ = null; + + +/** + * Last URI. + * @type {goog.Uri} + * @private + */ +goog.testing.net.MockIFrameIo.prototype.lastUri_ = null; + + +/** @private {Function} */ +goog.testing.net.MockIFrameIo.prototype.errorChecker_; + + +/** @private {boolean} */ +goog.testing.net.MockIFrameIo.prototype.success_; + + +/** @private {boolean} */ +goog.testing.net.MockIFrameIo.prototype.complete_; + + +/** + * Simulates the iframe send. + * + * @param {goog.Uri|string} uri Uri of the request. + * @param {string=} opt_method Default is GET, POST uses a form to submit the + * request. + * @param {boolean=} opt_noCache Append a timestamp to the request to avoid + * caching. + * @param {Object|goog.structs.Map=} opt_data Map of key-value pairs. + */ +goog.testing.net.MockIFrameIo.prototype.send = function( + uri, opt_method, opt_noCache, opt_data) { + if (this.active_) { + throw new Error('[goog.net.IframeIo] Unable to send, already active.'); + } + + this.testQueue_.enqueue(['s', uri, opt_method, opt_noCache, opt_data]); + this.complete_ = false; + this.active_ = true; +}; + + +/** + * Simulates the iframe send from a form. + * @param {Element} form Form element used to send the request to the server. + * @param {string=} opt_uri Uri to set for the destination of the request, by + * default the uri will come from the form. + * @param {boolean=} opt_noCache Append a timestamp to the request to avoid + * caching. + */ +goog.testing.net.MockIFrameIo.prototype.sendFromForm = function( + form, opt_uri, opt_noCache) { + if (this.active_) { + throw new Error('[goog.net.IframeIo] Unable to send, already active.'); + } + + this.testQueue_.enqueue(['s', form, opt_uri, opt_noCache]); + this.complete_ = false; + this.active_ = true; +}; + + +/** + * Simulates aborting the current Iframe request. + * @param {goog.net.ErrorCode=} opt_failureCode Optional error code to use - + * defaults to ABORT. + */ +goog.testing.net.MockIFrameIo.prototype.abort = function(opt_failureCode) { + if (this.active_) { + this.testQueue_.enqueue(['a', opt_failureCode]); + this.complete_ = false; + this.active_ = false; + this.success_ = false; + this.lastErrorCode_ = opt_failureCode || goog.net.ErrorCode.ABORT; + this.dispatchEvent(goog.net.EventType.ABORT); + this.simulateReady(); + } +}; + + +/** + * Simulates receive of incremental data. + * @param {Object} data Data. + */ +goog.testing.net.MockIFrameIo.prototype.simulateIncrementalData = + function(data) { + this.dispatchEvent(new goog.net.IframeIo.IncrementalDataEvent(data)); +}; + + +/** + * Simulates the iframe is done. + * @param {goog.net.ErrorCode} errorCode The error code for any error that + * should be simulated. + */ +goog.testing.net.MockIFrameIo.prototype.simulateDone = function(errorCode) { + if (errorCode) { + this.success_ = false; + this.lastErrorCode_ = goog.net.ErrorCode.HTTP_ERROR; + this.lastError_ = this.getLastError(); + this.dispatchEvent(goog.net.EventType.ERROR); + } else { + this.success_ = true; + this.lastErrorCode_ = goog.net.ErrorCode.NO_ERROR; + this.dispatchEvent(goog.net.EventType.SUCCESS); + } + this.complete_ = true; + this.dispatchEvent(goog.net.EventType.COMPLETE); +}; + + +/** + * Simulates the IFrame is ready for the next request. + */ +goog.testing.net.MockIFrameIo.prototype.simulateReady = function() { + this.dispatchEvent(goog.net.EventType.READY); +}; + + +/** + * @return {boolean} True if transfer is complete. + */ +goog.testing.net.MockIFrameIo.prototype.isComplete = function() { + return this.complete_; +}; + + +/** + * @return {boolean} True if transfer was successful. + */ +goog.testing.net.MockIFrameIo.prototype.isSuccess = function() { + return this.success_; +}; + + +/** + * @return {boolean} True if a transfer is in progress. + */ +goog.testing.net.MockIFrameIo.prototype.isActive = function() { + return this.active_; +}; + + +/** + * Returns the last response text (i.e. the text content of the iframe). + * Assumes plain text! + * @return {string} Result from the server. + */ +goog.testing.net.MockIFrameIo.prototype.getResponseText = function() { + return this.lastContent_; +}; + + +/** + * Parses the content as JSON. This is a safe parse and may throw an error + * if the response is malformed. + * @return {Object} The parsed content. + */ +goog.testing.net.MockIFrameIo.prototype.getResponseJson = function() { + return /** @type {!Object} */ (JSON.parse(this.lastContent_)); +}; + + +/** + * Get the uri of the last request. + * @return {goog.Uri} Uri of last request. + */ +goog.testing.net.MockIFrameIo.prototype.getLastUri = function() { + return this.lastUri_; +}; + + +/** + * Gets the last error code. + * @return {goog.net.ErrorCode} Last error code. + */ +goog.testing.net.MockIFrameIo.prototype.getLastErrorCode = function() { + return this.lastErrorCode_; +}; + + +/** + * Gets the last error message. + * @return {string} Last error message. + */ +goog.testing.net.MockIFrameIo.prototype.getLastError = function() { + return goog.net.ErrorCode.getDebugMessage(this.lastErrorCode_); +}; + + +/** + * Gets the last custom error. + * @return {Object} Last custom error. + */ +goog.testing.net.MockIFrameIo.prototype.getLastCustomError = function() { + return this.lastCustomError_; +}; + + +/** + * Sets the callback function used to check if a loaded IFrame is in an error + * state. + * @param {Function} fn Callback that expects a document object as it's single + * argument. + */ +goog.testing.net.MockIFrameIo.prototype.setErrorChecker = function(fn) { + this.errorChecker_ = fn; +}; + + +/** + * Gets the callback function used to check if a loaded IFrame is in an error + * state. + * @return {Function} A callback that expects a document object as it's single + * argument. + */ +goog.testing.net.MockIFrameIo.prototype.getErrorChecker = function() { + return this.errorChecker_; +}; diff --git a/closure-library/closure/goog/testing/net/xhrio.js b/closure-library/closure/goog/testing/net/xhrio.js new file mode 100644 index 0000000000..cf815fc229 --- /dev/null +++ b/closure-library/closure/goog/testing/net/xhrio.js @@ -0,0 +1,952 @@ +// Copyright 2007 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 Mock of XhrIo for unit testing. + * @suppress {accessControls} Overriding private properties for test impl. + */ + +goog.setTestOnly('goog.testing.net.XhrIo'); +goog.provide('goog.testing.net.XhrIo'); + +goog.require('goog.Uri'); +goog.require('goog.array'); +goog.require('goog.dom.xml'); +goog.require('goog.events'); +goog.require('goog.net.ErrorCode'); +goog.require('goog.net.EventType'); +goog.require('goog.net.HttpStatus'); +goog.require('goog.net.XhrIo'); +goog.require('goog.net.XmlHttp'); +goog.require('goog.object'); +goog.require('goog.structs'); +goog.require('goog.structs.Map'); +goog.require('goog.testing.TestQueue'); +goog.require('goog.uri.utils'); + +/** + * Mock implementation of goog.net.XhrIo. This doesn't provide a mock + * implementation for all cases, but it's not too hard to add them as needed. + * @param {goog.testing.TestQueue=} opt_testQueue Test queue for inserting test + * events. + * @constructor + * @extends {goog.net.XhrIo} + */ +goog.testing.net.XhrIo = function(opt_testQueue) { + goog.testing.net.XhrIo.base.call(this); + + /** + * Map of default headers to add to every request, use: + * XhrIo.headers.set(name, value) + * @type {!goog.structs.Map} + */ + this.headers = new goog.structs.Map(); + + /** + * Queue of events write to. + * @private {?goog.testing.TestQueue} + */ + this.testQueue_ = opt_testQueue || null; +}; +goog.inherits(goog.testing.net.XhrIo, goog.net.XhrIo); + +/** + * Some compiled tests replace goog.net.XhrIo with goog.testing.net.XhrIo, + * which would cause a circular constructor loop. + * @nocollapse + */ +goog.testing.net.XhrIo.base = goog.net.XhrIo; + +/** + * To emulate the behavior of the actual XhrIo, we do not allow access to the + * XhrIo's properties outside the event callbacks. For backwards compatibility, + * we allow tests to allow access by setting this value to true. + * @type {boolean} + */ +goog.testing.net.XhrIo.allowUnsafeAccessToXhrIoOutsideCallbacks = false; + + +/** + * Alias this enum here to make mocking of goog.net.XhrIo easier. + * @enum {string} + */ +goog.testing.net.XhrIo.ResponseType = goog.net.XhrIo.ResponseType; + + +/** + * The pattern matching the 'http' and 'https' URI schemes. + * @private {!RegExp} + */ +goog.testing.net.XhrIo.HTTP_SCHEME_PATTERN_ = /^https?$/i; + + +/** + * All non-disposed instances of goog.testing.net.XhrIo created + * by {@link goog.testing.net.XhrIo.send} are in this Array. + * @see goog.testing.net.XhrIo.cleanup + * @type {!Array} + * @private + */ +goog.testing.net.XhrIo.sendInstances_ = []; + + +/** + * Returns an Array containing all non-disposed instances of + * goog.testing.net.XhrIo created by {@link goog.testing.net.XhrIo.send}. + * @return {!Array} Array of goog.testing.net.XhrIo + * instances. + */ +goog.testing.net.XhrIo.getSendInstances = function() { + return goog.testing.net.XhrIo.sendInstances_; +}; + + +/** + * Disposes all non-disposed instances of goog.testing.net.XhrIo created by + * {@link goog.testing.net.XhrIo.send}. + * @see goog.net.XhrIo.cleanup + */ +goog.testing.net.XhrIo.cleanup = function() { + var instances = goog.testing.net.XhrIo.sendInstances_; + while (instances.length) { + instances.pop().dispose(); + } +}; + + +/** + * Simulates the static XhrIo send method. + * @param {string} url Uri to make request to. + * @param {Function=} opt_callback Callback function for when request is + * complete. + * @param {string=} opt_method Send method, default: GET. + * @param {ArrayBuffer|ArrayBufferView|Blob|Document|FormData|string=} + * opt_content Body data. + * @param {Object|goog.structs.Map=} opt_headers Map of headers to add to the + * request. + * @param {number=} opt_timeoutInterval Number of milliseconds after which an + * incomplete request will be aborted; 0 means no timeout is set. + * @param {boolean=} opt_withCredentials Whether to send credentials with the + * request. Default to false. See {@link goog.net.XhrIo#setWithCredentials}. + * @return {!goog.testing.net.XhrIo} The mocked sent XhrIo. + */ +goog.testing.net.XhrIo.send = function( + url, opt_callback, opt_method, opt_content, opt_headers, + opt_timeoutInterval, opt_withCredentials) { + var x = new goog.testing.net.XhrIo(); + goog.testing.net.XhrIo.sendInstances_.push(x); + if (opt_callback) { + goog.events.listen(x, goog.net.EventType.COMPLETE, opt_callback); + } + goog.events.listen( + x, goog.net.EventType.READY, + goog.partial(goog.testing.net.XhrIo.cleanupSend_, x)); + if (opt_timeoutInterval) { + x.setTimeoutInterval(opt_timeoutInterval); + } + x.setWithCredentials(Boolean(opt_withCredentials)); + x.send(url, opt_method, opt_content, opt_headers); + + return x; +}; + + +/** + * Disposes of the specified goog.testing.net.XhrIo created by + * {@link goog.testing.net.XhrIo.send} and removes it from + * {@link goog.testing.net.XhrIo.pendingStaticSendInstances_}. + * @param {!goog.testing.net.XhrIo} XhrIo An XhrIo created by + * {@link goog.testing.net.XhrIo.send}. + * @private + */ +goog.testing.net.XhrIo.cleanupSend_ = function(XhrIo) { + XhrIo.dispose(); + goog.array.remove(goog.testing.net.XhrIo.sendInstances_, XhrIo); +}; + + +/** + * Stores the simulated response headers for the requests which are sent through + * this XhrIo. + * @type {Object} + * @private + */ +goog.testing.net.XhrIo.prototype.responseHeaders_; + + +/** + * Whether MockXhrIo is active. + * @private {boolean} + * @override + */ +goog.testing.net.XhrIo.prototype.active_ = false; + + +/** + * Last URI that was requested. + * @private {?goog.Uri|string} + * @override + */ +goog.testing.net.XhrIo.prototype.lastUri_ = ''; + + +/** + * Last HTTP method that was requested. + * @private {string|undefined} + * @override + */ +goog.testing.net.XhrIo.prototype.lastMethod_; + + +/** + * Last POST content that was requested. + * @private { + * ArrayBuffer|ArrayBufferView|Blob|Document|FormData|string|undefined} + */ +goog.testing.net.XhrIo.prototype.lastContent_; + + +/** + * Additional headers that were requested in the last query. + * @private {Object|goog.structs.Map|undefined} + */ +goog.testing.net.XhrIo.prototype.lastHeaders_; + + +/** + * Last error code. + * @private {!goog.net.ErrorCode} + * @override + */ +goog.testing.net.XhrIo.prototype.lastErrorCode_ = goog.net.ErrorCode.NO_ERROR; + + +/** + * Last error message. + * @private {string} + * @override + */ +goog.testing.net.XhrIo.prototype.lastError_ = ''; + + +/** + * The response object. + * @private {string|Document|ArrayBuffer} + */ +goog.testing.net.XhrIo.prototype.response_ = ''; + + +/** + * The status code. + * @private {number} + */ +goog.testing.net.XhrIo.prototype.statusCode_ = 0; + + +/** + * Mock ready state. + * @private {number} + */ +goog.testing.net.XhrIo.prototype.readyState_ = + goog.net.XmlHttp.ReadyState.UNINITIALIZED; + + +/** + * Number of milliseconds after which an incomplete request will be aborted and + * a {@link goog.net.EventType.TIMEOUT} event raised; 0 means no timeout is set. + * @private {number} + * @override + */ +goog.testing.net.XhrIo.prototype.timeoutInterval_ = 0; + + +/** + * The requested type for the response. The empty string means use the default + * XHR behavior. + * @private {goog.net.XhrIo.ResponseType} + * @override + */ +goog.testing.net.XhrIo.prototype.responseType_ = + goog.net.XhrIo.ResponseType.DEFAULT; + + +/** + * Whether a "credentialed" request is to be sent (one that is aware of cookies + * and authentication) . This is applicable only for cross-domain requests and + * more recent browsers that support this part of the HTTP Access Control + * standard. + * + * @see http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#withcredentials + * + * @private {boolean} + * @override + */ +goog.testing.net.XhrIo.prototype.withCredentials_ = false; + + +/** + * Whether progress events shall be sent for this request. + * + * @private {boolean} + * @override + */ +goog.testing.net.XhrIo.prototype.progressEventsEnabled_ = false; + + +/** + * Whether there's currently an underlying XHR object. + * @private {boolean} + */ +goog.testing.net.XhrIo.prototype.hasXhr_ = false; + + +/** + * Returns the number of milliseconds after which an incomplete request will be + * aborted, or 0 if no timeout is set. + * @return {number} Timeout interval in milliseconds. + * @override + */ +goog.testing.net.XhrIo.prototype.getTimeoutInterval = function() { + return this.timeoutInterval_; +}; + + +/** + * Sets the number of milliseconds after which an incomplete request will be + * aborted and a {@link goog.net.EventType.TIMEOUT} event raised; 0 means no + * timeout is set. + * @param {number} ms Timeout interval in milliseconds; 0 means none. + * @override + */ +goog.testing.net.XhrIo.prototype.setTimeoutInterval = function(ms) { + this.timeoutInterval_ = Math.max(0, ms); +}; + + +/** + * Causes timeout events to be fired. + */ +goog.testing.net.XhrIo.prototype.simulateTimeout = function() { + this.lastErrorCode_ = goog.net.ErrorCode.TIMEOUT; + this.dispatchEvent(goog.net.EventType.TIMEOUT); + this.abort(goog.net.ErrorCode.TIMEOUT); +}; + + +/** + * Sets the desired type for the response. At time of writing, this is only + * supported in very recent versions of WebKit (10.0.612.1 dev and later). + * + * If this is used, the response may only be accessed via {@link #getResponse}. + * + * @param {goog.net.XhrIo.ResponseType} type The desired type for the response. + * @override + */ +goog.testing.net.XhrIo.prototype.setResponseType = function(type) { + this.responseType_ = type; +}; + + +/** + * Gets the desired type for the response. + * @return {!goog.net.XhrIo.ResponseType} The desired type for the response. + * @override + */ +goog.testing.net.XhrIo.prototype.getResponseType = function() { + return this.responseType_; +}; + + +/** + * Sets whether a "credentialed" request that is aware of cookie and + * authentication information should be made. This option is only supported by + * browsers that support HTTP Access Control. As of this writing, this option + * is not supported in IE. + * + * @param {boolean} withCredentials Whether this should be a "credentialed" + * request. + * @override + */ +goog.testing.net.XhrIo.prototype.setWithCredentials = function( + withCredentials) { + this.withCredentials_ = withCredentials; +}; + + +/** + * Gets whether a "credentialed" request is to be sent. + * @return {boolean} The desired type for the response. + * @override + */ +goog.testing.net.XhrIo.prototype.getWithCredentials = function() { + return this.withCredentials_; +}; + + +/** + * Sets whether progress events are enabled for this request. Note + * that progress events require pre-flight OPTIONS request handling + * for CORS requests, and may cause trouble with older browsers. See + * goog.net.XhrIo.progressEventsEnabled_ for details. + * @param {boolean} enabled Whether progress events should be enabled. + * @override + */ +goog.testing.net.XhrIo.prototype.setProgressEventsEnabled = function(enabled) { + this.progressEventsEnabled_ = enabled; +}; + + +/** + * Gets whether progress events are enabled. + * @return {boolean} Whether progress events are enabled for this request. + * @override + */ +goog.testing.net.XhrIo.prototype.getProgressEventsEnabled = function() { + return this.progressEventsEnabled_; +}; + + +/** + * Abort the current XMLHttpRequest + * @param {!goog.net.ErrorCode=} opt_failureCode Optional error code to use - + * defaults to ABORT. + * @override + */ +goog.testing.net.XhrIo.prototype.abort = function(opt_failureCode) { + if (this.active_) { + try { + this.active_ = false; + this.readyState_ = goog.net.XmlHttp.ReadyState.UNINITIALIZED; + this.statusCode_ = -1; + this.lastErrorCode_ = opt_failureCode || goog.net.ErrorCode.ABORT; + this.dispatchEvent(goog.net.EventType.COMPLETE); + this.dispatchEvent(goog.net.EventType.ABORT); + } finally { + this.simulateReady(); + } + } +}; + + +/** + * Simulates the XhrIo send. + * @param {?goog.Uri|string} url Uri to make request too. + * @param {string=} opt_method Send method, default: GET. + * @param {ArrayBuffer|ArrayBufferView|Blob|Document|FormData|string=} + * opt_content Body data. + * @param {Object|goog.structs.Map=} opt_headers Map of headers to add to the + * request. + * @override + */ +goog.testing.net.XhrIo.prototype.send = function( + url, opt_method, opt_content, opt_headers) { + if (this.hasXhr_) { + throw new Error('[goog.net.XhrIo] Object is active with another request'); + } + + this.lastUri_ = url; + this.lastMethod_ = opt_method || 'GET'; + this.lastContent_ = opt_content; + if (!this.headers.isEmpty()) { + this.lastHeaders_ = this.headers.toObject(); + // Add headers specific to this request + if (opt_headers) { + goog.structs.forEach(opt_headers, goog.bind(function(value, key) { + this.lastHeaders_[key] = value; + }, this)); + } + } else { + this.lastHeaders_ = opt_headers; + } + + if (this.testQueue_) { + this.testQueue_.enqueue(['s', url, opt_method, opt_content, opt_headers]); + } + this.hasXhr_ = true; + this.active_ = true; + this.readyState_ = goog.net.XmlHttp.ReadyState.UNINITIALIZED; + this.simulateReadyStateChange(goog.net.XmlHttp.ReadyState.LOADING); +}; + + +/** + * Creates a new XHR object. + * @return {!goog.net.XhrLike.OrNative} The newly created XHR object. + * @override + */ +goog.testing.net.XhrIo.prototype.createXhr = function() { + return goog.net.XmlHttp(); +}; + + +/** + * Simulates changing to the new ready state. + * @param {number} readyState Ready state to change to. + */ +goog.testing.net.XhrIo.prototype.simulateReadyStateChange = function( + readyState) { + if (readyState < this.readyState_) { + throw new Error('Readystate cannot go backwards'); + } + + // INTERACTIVE can be dispatched repeatedly as more data is reported. + if (readyState == goog.net.XmlHttp.ReadyState.INTERACTIVE && + readyState == this.readyState_) { + this.dispatchEvent(goog.net.EventType.READY_STATE_CHANGE); + return; + } + + while (this.readyState_ < readyState) { + this.readyState_++; + this.dispatchEvent(goog.net.EventType.READY_STATE_CHANGE); + + if (this.readyState_ == goog.net.XmlHttp.ReadyState.COMPLETE) { + this.active_ = false; + this.dispatchEvent(goog.net.EventType.COMPLETE); + } + } +}; + + +/** + * Simulate receiving some bytes but the request not fully completing, and + * the XHR entering the 'INTERACTIVE' state. + * @param {string} partialResponse A string to append to the response text. + * @param {Object=} opt_headers Simulated response headers. + */ +goog.testing.net.XhrIo.prototype.simulatePartialResponse = function( + partialResponse, opt_headers) { + this.response_ += partialResponse; + this.responseHeaders_ = opt_headers || {}; + this.statusCode_ = 200; + this.simulateReadyStateChange(goog.net.XmlHttp.ReadyState.INTERACTIVE); +}; + + +/** + * Simulates receiving a response. + * @param {number} statusCode Simulated status code. + * @param {string|Document|ArrayBuffer|null} response Simulated response. + * @param {Object=} opt_headers Simulated response headers. + */ +goog.testing.net.XhrIo.prototype.simulateResponse = function( + statusCode, response, opt_headers) { + // This library allows a response to be simulated without send ever being + // called. If there are no send instances, then just pretend that xhr_ and + // active_ have been set to true. + if (!goog.testing.net.XhrIo.allowUnsafeAccessToXhrIoOutsideCallbacks && + !goog.testing.net.XhrIo.sendInstances_.length) { + this.hasXhr_ = true; + this.active_ = true; + } + this.statusCode_ = statusCode; + this.response_ = response || ''; + this.responseHeaders_ = opt_headers || {}; + + try { + if (this.isSuccess()) { + this.simulateReadyStateChange(goog.net.XmlHttp.ReadyState.COMPLETE); + this.dispatchEvent(goog.net.EventType.SUCCESS); + } else { + this.lastErrorCode_ = goog.net.ErrorCode.HTTP_ERROR; + this.lastError_ = this.getStatusText() + ' [' + this.getStatus() + ']'; + this.simulateReadyStateChange(goog.net.XmlHttp.ReadyState.COMPLETE); + this.dispatchEvent(goog.net.EventType.ERROR); + } + } finally { + this.simulateReady(); + } +}; + + +/** + * Simulates the Xhr is ready for the next request. + */ +goog.testing.net.XhrIo.prototype.simulateReady = function() { + this.active_ = false; + this.hasXhr_ = false; + this.dispatchEvent(goog.net.EventType.READY); +}; + + +/** + * Simulates the Xhr progress event. + * @param {boolean} lengthComputable Whether progress is measurable. + * @param {number} loaded Amount of work already performed. + * @param {number} total Total amount of work to perform. + * @param {boolean=} opt_isDownload Whether the progress is from a download or + * upload. + */ +goog.testing.net.XhrIo.prototype.simulateProgress = function( + lengthComputable, loaded, total, opt_isDownload) { + /** + * @typedef {{ + * type: goog.net.EventType, + * lengthComputable: boolean, + * loaded: number, + * total: number + * }} + */ + var ProgressEventType; + + var /** ProgressEventType */ progressEvent = { + type: goog.net.EventType.PROGRESS, + lengthComputable: lengthComputable, + loaded: loaded, + total: total + }; + this.dispatchEvent(progressEvent); + var specificProgress = + /** @type {ProgressEventType} */ (goog.object.clone(progressEvent)); + specificProgress.type = opt_isDownload ? + goog.net.EventType.DOWNLOAD_PROGRESS : + goog.net.EventType.UPLOAD_PROGRESS; + this.dispatchEvent(specificProgress); +}; + + +/** + * @return {boolean} Whether there is an active request. + * @override + */ +goog.testing.net.XhrIo.prototype.isActive = function() { + return !!this.hasXhr_; +}; + + +/** + * Has the request completed. + * @return {boolean} Whether the request has completed. + * @override + */ +goog.testing.net.XhrIo.prototype.isComplete = function() { + return this.readyState_ == goog.net.XmlHttp.ReadyState.COMPLETE; +}; + + +/** + * Has the request compeleted with a success. + * @return {boolean} Whether the request compeleted successfully. + * @override + */ +goog.testing.net.XhrIo.prototype.isSuccess = function() { + var status = this.getStatus(); + // A zero status code is considered successful for local files. + return goog.net.HttpStatus.isSuccess(status) || + status === 0 && !this.isLastUriEffectiveSchemeHttp_(); +}; + + +/** + * @return {boolean} whether the effective scheme of the last URI that was + * fetched was 'http' or 'https'. + * @private + * @override + */ +goog.testing.net.XhrIo.prototype.isLastUriEffectiveSchemeHttp_ = function() { + var scheme = goog.uri.utils.getEffectiveScheme(String(this.lastUri_)); + return goog.testing.net.XhrIo.HTTP_SCHEME_PATTERN_.test(scheme); +}; + + +/** + * Returns the readystate. + * @return {!goog.net.XmlHttp.ReadyState} goog.net.XmlHttp.ReadyState.*. + * @override + */ +goog.testing.net.XhrIo.prototype.getReadyState = function() { + return /** @type {!goog.net.XmlHttp.ReadyState} */ (this.readyState_); +}; + + +/** + * Get the status from the Xhr object. Will only return correct result when + * called from the context of a callback. + * @return {number} Http status. + * @override + */ +goog.testing.net.XhrIo.prototype.getStatus = function() { + return this.statusCode_; +}; + + +/** + * Get the status text from the Xhr object. Will only return correct result + * when called from the context of a callback. + * @return {string} Status text. + * @override + */ +goog.testing.net.XhrIo.prototype.getStatusText = function() { + return ''; +}; + + +/** + * Gets the last error message. + * @return {!goog.net.ErrorCode} Last error code. + * @override + */ +goog.testing.net.XhrIo.prototype.getLastErrorCode = function() { + return this.lastErrorCode_; +}; + + +/** + * Gets the last error message. + * @return {string} Last error message. + * @override + */ +goog.testing.net.XhrIo.prototype.getLastError = function() { + return this.lastError_; +}; + + +/** + * Gets the last URI that was requested. + * @return {string} Last URI. + * @override + */ +goog.testing.net.XhrIo.prototype.getLastUri = function() { + // A few tests depend on this returning a goog.Uri object, even though + // goog.net.XhrIo only ever returns a string from getLastUri. + // TODO(closure-team): Update the tests that are using getLastUri for + // null or goog.Uri return values. + return /** @type {string} */ (this.lastUri_); +}; + + +/** + * Gets the last HTTP method that was requested. + * @return {string|undefined} Last HTTP method used by send. + */ +goog.testing.net.XhrIo.prototype.getLastMethod = function() { + return this.lastMethod_; +}; + + +/** + * Gets the last POST content that was requested. + * @return {ArrayBuffer|ArrayBufferView|Blob|Document|FormData|string|undefined} + * Last POST content or undefined if last request was a GET. + */ +goog.testing.net.XhrIo.prototype.getLastContent = function() { + return this.lastContent_; +}; + + +/** + * Gets the headers of the last request. + * @return {Object|goog.structs.Map|undefined} Last headers manually set in send + * call or undefined if no additional headers were specified. + */ +goog.testing.net.XhrIo.prototype.getLastRequestHeaders = function() { + return this.lastHeaders_; +}; + + +/** + * Returns true if there is a valid xhr, or if + * allowUnsafeAccessToXhrIoOutsideCallbacks is false. + * @return {boolean} + * @private + */ +goog.testing.net.XhrIo.prototype.checkXhr_ = function() { + return ( + goog.testing.net.XhrIo.allowUnsafeAccessToXhrIoOutsideCallbacks || + !!this.hasXhr_); +}; + + +/** + * Gets the response text from the Xhr object. Will only return correct result + * when called from the context of a callback. + * @return {string} Result from the server. + * @override + */ +goog.testing.net.XhrIo.prototype.getResponseText = function() { + if (!this.checkXhr_()) { + return ''; + } else if (goog.isString(this.response_)) { + return this.response_; + } else if ( + goog.global['ArrayBuffer'] && this.response_ instanceof ArrayBuffer) { + return ''; + } else { + return goog.dom.xml.serialize(/** @type {Document} */ (this.response_)); + } +}; + + +/** + * Gets the response body from the Xhr object. Will only return correct result + * when called from the context of a callback. + * @return {Object} Binary result from the server or null. + * @override + */ +goog.testing.net.XhrIo.prototype.getResponseBody = function() { + return null; +}; + + +/** + * Gets the response and evaluates it as JSON from the Xhr object. Will only + * return correct result when called from the context of a callback. + * @param {string=} opt_xssiPrefix Optional XSSI prefix string to use for + * stripping of the response before parsing. This needs to be set only if + * your backend server prepends the same prefix string to the JSON response. + * @return {Object|undefined} JavaScript object. + * @throws Error if s is invalid JSON. + * @override + */ +goog.testing.net.XhrIo.prototype.getResponseJson = function(opt_xssiPrefix) { + if (!this.checkXhr_()) { + return undefined; + } + + var responseText = this.getResponseText(); + if (opt_xssiPrefix && responseText.indexOf(opt_xssiPrefix) == 0) { + responseText = responseText.substring(opt_xssiPrefix.length); + } + + return /** @type {!Object} */ (JSON.parse(responseText)); +}; + + +/** + * Gets the response XML from the Xhr object. Will only return correct result + * when called from the context of a callback. + * @return {Document} Result from the server if it was XML. + * @override + */ +goog.testing.net.XhrIo.prototype.getResponseXml = function() { + if (!this.checkXhr_()) { + return null; + } + // NOTE(user): I haven't found out how to check in Internet Explorer + // whether the response is XML document, so I do it the other way around. + return goog.isString(this.response_) || + (goog.global['ArrayBuffer'] && + this.response_ instanceof ArrayBuffer) ? + null : + /** @type {Document} */ (this.response_); +}; + + +/** + * Get the response as the type specificed by {@link #setResponseType}. At time + * of writing, this is only supported in very recent versions of WebKit + * (10.0.612.1 dev and later). + * + * @return {*} The response. + * @override + */ +goog.testing.net.XhrIo.prototype.getResponse = function() { + return this.checkXhr_() ? this.response_ : null; +}; + + +/** + * Get the value of the response-header with the given name from the Xhr object + * Will only return correct result when called from the context of a callback + * and the request has completed + * @param {string} key The name of the response-header to retrieve. + * @return {string|undefined} The value of the response-header named key. + * @override + */ +goog.testing.net.XhrIo.prototype.getResponseHeader = function(key) { + if (!this.checkXhr_() || !this.isComplete()) { + return undefined; + } + return this.responseHeaders_[key]; +}; + + +/** + * Gets the text of all the headers in the response. + * Will only return correct result when called from the context of a callback + * and the request has completed + * @return {string} The string containing all the response headers. + * @override + */ +goog.testing.net.XhrIo.prototype.getAllResponseHeaders = function() { + if (!this.checkXhr_() || !this.isComplete()) { + return ''; + } + return this.getAllStreamingResponseHeaders(); +}; + + +/** + * Returns all response headers as a key-value map. + * Multiple values for the same header key can be combined into one, + * separated by a comma and a space. + * Note that the native getResponseHeader method for retrieving a single header + * does a case insensitive match on the header name. This method does not + * include any case normalization logic, it will just return a key-value + * representation of the headers. + * See: http://www.w3.org/TR/XMLHttpRequest/#the-getresponseheader()-method + * @return {!Object} An object with the header keys as keys + * and header values as values. + * @override + */ +goog.testing.net.XhrIo.prototype.getResponseHeaders = function() { + if (!this.checkXhr_() || !this.isComplete()) { + return {}; + } + var headersObject = {}; + goog.object.forEach(this.responseHeaders_, function(value, key) { + if (headersObject[key]) { + headersObject[key] += ', ' + value; + } else { + headersObject[key] = value; + } + }); + return headersObject; +}; + + +/** + * Get the value of the response-header with the given name from the Xhr object. + * As opposed to {@link #getResponseHeader}, this method does not require that + * the request has completed. + * @param {string} key The name of the response-header to retrieve. + * @return {?string} The value of the response-header, or null if it is + * unavailable. + * @override + */ +goog.testing.net.XhrIo.prototype.getStreamingResponseHeader = function(key) { + if (!this.checkXhr_()) { + return null; + } + return key in this.responseHeaders_ ? this.responseHeaders_[key] : null; +}; + + +/** + * Gets the text of all the headers in the response. As opposed to + * {@link #getAllResponseHeaders}, this method does not require that the request + * has completed. + * @return {string} The value of the response headers or empty string. + * @override + */ +goog.testing.net.XhrIo.prototype.getAllStreamingResponseHeaders = function() { + if (!this.checkXhr_()) { + return ''; + } + var headers = []; + goog.object.forEach(this.responseHeaders_, function(value, name) { + headers.push(name + ': ' + value); + }); + return headers.join('\r\n'); +}; diff --git a/closure-library/closure/goog/testing/net/xhriopool.js b/closure-library/closure/goog/testing/net/xhriopool.js new file mode 100644 index 0000000000..b9ed7fe260 --- /dev/null +++ b/closure-library/closure/goog/testing/net/xhriopool.js @@ -0,0 +1,66 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview An XhrIo pool that uses a single mock XHR object for testing. + * + */ + +goog.setTestOnly('goog.testing.net.XhrIoPool'); +goog.provide('goog.testing.net.XhrIoPool'); + +goog.require('goog.net.XhrIoPool'); +goog.require('goog.testing.net.XhrIo'); + + + +/** + * A pool containing a single mock XhrIo object. + * + * @param {goog.testing.net.XhrIo=} opt_xhr The mock XhrIo object. + * @constructor + * @extends {goog.net.XhrIoPool} + * @final + */ +goog.testing.net.XhrIoPool = function(opt_xhr) { + /** + * The mock XhrIo object. + * @type {!goog.testing.net.XhrIo} + * @private + */ + this.xhr_ = opt_xhr || new goog.testing.net.XhrIo(); + + // Run this after setting xhr_ because xhr_ is used to initialize the pool. + goog.testing.net.XhrIoPool.base(this, 'constructor', undefined, 1, 1); +}; +goog.inherits(goog.testing.net.XhrIoPool, goog.net.XhrIoPool); + + +/** + * @override + * @suppress {invalidCasts} + */ +goog.testing.net.XhrIoPool.prototype.createObject = function() { + return (/** @type {!goog.net.XhrIo} */ (this.xhr_)); +}; + + +/** + * Get the mock XhrIo used by this pool. + * + * @return {!goog.testing.net.XhrIo} The mock XhrIo. + */ +goog.testing.net.XhrIoPool.prototype.getXhr = function() { + return this.xhr_; +}; diff --git a/closure-library/closure/goog/testing/objectpropertystring.js b/closure-library/closure/goog/testing/objectpropertystring.js new file mode 100644 index 0000000000..26cb644f2e --- /dev/null +++ b/closure-library/closure/goog/testing/objectpropertystring.js @@ -0,0 +1,70 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Helper for passing property names as string literals in + * compiled test code. + * + */ + +goog.setTestOnly('goog.testing.ObjectPropertyString'); +goog.provide('goog.testing.ObjectPropertyString'); + + + +/** + * Object to pass a property name as a string literal and its containing object + * when the JSCompiler is rewriting these names. This should only be used in + * test code. + * + * @param {Object} object The containing object. + * @param {Object|string} propertyString Property name as a string literal. + * @constructor + * @final + * @deprecated Use goog.reflect.objectProperty instead. + */ +goog.testing.ObjectPropertyString = function(object, propertyString) { + this.object_ = object; + this.propertyString_ = /** @type {string} */ (propertyString); +}; + + +/** + * @type {Object} + * @private + */ +goog.testing.ObjectPropertyString.prototype.object_; + + +/** + * @type {string} + * @private + */ +goog.testing.ObjectPropertyString.prototype.propertyString_; + + +/** + * @return {Object} The object. + */ +goog.testing.ObjectPropertyString.prototype.getObject = function() { + return this.object_; +}; + + +/** + * @return {string} The property string. + */ +goog.testing.ObjectPropertyString.prototype.getPropertyString = function() { + return this.propertyString_; +}; diff --git a/closure-library/closure/goog/testing/parallel_closure_test_suite.js b/closure-library/closure/goog/testing/parallel_closure_test_suite.js new file mode 100644 index 0000000000..41f0efe6de --- /dev/null +++ b/closure-library/closure/goog/testing/parallel_closure_test_suite.js @@ -0,0 +1,153 @@ +// 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 Parallel closure_test_suite test file. This test is not + * intended to be ran or depended on directly. + * + */ + +goog.module('goog.testing.parallelClosureTestSuite'); +goog.setTestOnly('goog.testing.parallelClosureTestSuite'); + +var MultiTestRunner = goog.require('goog.testing.MultiTestRunner'); +var Promise = goog.require('goog.Promise'); +var TestCase = goog.require('goog.testing.TestCase'); +var asserts = goog.require('goog.asserts'); +var events = goog.require('goog.events'); +var json = goog.require('goog.json'); +var testSuite = goog.require('goog.testing.testSuite'); + +/** @type {?MultiTestRunner} */ +var testRunner = null; + + +/** + * @typedef {{ + * totalTests: number, + * totalFailures: number, + * failureReports: string, + * allResults: !Object> + * }} + */ +var ParallelTestResults; + + +/** + * Processes the test results returned from MultiTestRunner and creates a + * consolidated result object that the test runner understands. + * @param {!Array>>} testResults The list of + * individual test results from MultiTestRunner. + * @return {!ParallelTestResults} Flattened test report for all tests. + */ +function processAllTestResults(testResults) { + var totalTests = 0; + var totalFailed = 0; + var allResults = {}; + var failureReports = ''; + + for (var i = 0; i < testResults.length; i++) { + var result = testResults[i]; + for (var testName in result) { + totalTests++; + allResults[testName] = result[testName]; + var failures = result[testName]; + if (failures.length) { + totalFailed++; + for (var j = 0; j < failures.length; j++) { + failureReports += failures[j] + '\n'; + } + } + } + } + + return { + totalTests: totalTests, + totalFailures: totalFailed, + failureReports: failureReports, + allResults: allResults + }; +} + +var testObj = { + setUpPage: function() { + // G_parallelTestRunner is exported in gen_parallel_test_html.py. + var timeout = goog.global['G_parallelTestRunner']['testTimeout']; + var allTests = goog.global['G_parallelTestRunner']['allTests']; + var parallelFrames = goog.global['G_parallelTestRunner']['parallelFrames']; + var parallelTimeout = + goog.global['G_parallelTestRunner']['parallelTimeout']; + + // Create a test runner and render it. + testRunner = new MultiTestRunner() + .setName(document.title) + .setBasePath('/google3/') + .setPoolSize(parallelFrames) + .setStatsBucketSizes(5, 500) + .setTimeout(timeout * 1000) + .addTests(allTests); + + testRunner.render(document.getElementById('runner')); + + // There's only a single test method that runs all the tests, so this + // promiseTimeout is effectively the timeout of the entire test suite + TestCase.getActiveTestCase().promiseTimeout = parallelTimeout * 1000; + + // Return testRunner for testing purposes. + return testRunner; + }, + + testRunAllTests: function() { + asserts.assert(testRunner, 'Was "setUpPage" called?'); + + var failurePromise = new Promise(function(resolve, reject) { + events.listen(testRunner, 'testsFinished', resolve); + }); + + testRunner.start(); + + var allResults = {}; + // TestPoller.java invokes this to get test results for sponge. We override + // it and return the results of each individual test instead of the + // containing "testRunAllTests". + window['G_testRunner']['getTestResults'] = function() { + return allResults; + }; + + window['G_testRunner']['getTestResultsAsJson'] = function() { + return json.serialize(allResults); + }; + + return failurePromise.then(function(failures) { + var testResults = processAllTestResults(failures['allTestResults']); + allResults = testResults.allResults; + if (testResults.totalFailures) { + fail( + testResults.totalFailures + ' of ' + testResults.totalTests + + ' test(s) failed!\n\n' + testResults.failureReports); + } + }); + } +}; + +// G_parallelTestRunner should only be present when being run from a parallel +// closure_test_suite target. If it's not present, we're including this file +// to be unit tested. +if (goog.global['G_parallelTestRunner']) { + testSuite(testObj); +} + +// Export test methods/vars so they can also be tested. +testObj['processAllTestResults'] = processAllTestResults; +exports = testObj; diff --git a/closure-library/closure/goog/testing/performancetable.css b/closure-library/closure/goog/testing/performancetable.css new file mode 100644 index 0000000000..28056d0264 --- /dev/null +++ b/closure-library/closure/goog/testing/performancetable.css @@ -0,0 +1,46 @@ +/* + * Copyright 2009 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* Author: attila@google.com (Attila Bodis) */ +/* Author: nicksantos@google.com (Nick Santos) */ + +table.test-results { + font: 12px "Courier New", Courier, monospace; +} + +table.test-results thead th { + padding: 2px; + color: #fff; + background-color: #369; + text-align: center; +} + +table.test-results tbody td { + padding: 2px; + color: #333; + background-color: #ffc; + text-align: right; +} + +table.test-results tbody td.test-description { + text-align: left; +} + +table.test-results tbody td.test-average { + color: #000; + font-weight: bold; +} + +table.test-results tbody tr.test-suspicious td.test-standard-deviation { + color: #800; +} + +table.test-results tbody td.test-error { + color: #800; + font-weight: bold; + text-align: center; +} diff --git a/closure-library/closure/goog/testing/performancetable.js b/closure-library/closure/goog/testing/performancetable.js new file mode 100644 index 0000000000..90eb68a412 --- /dev/null +++ b/closure-library/closure/goog/testing/performancetable.js @@ -0,0 +1,205 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A table for showing the results of performance testing. + * + * {@see goog.testing.benchmark} for an easy way to use this functionality. + * + * @author attila@google.com (Attila Bodis) + * @author nicksantos@google.com (Nick Santos) + */ + +goog.setTestOnly('goog.testing.PerformanceTable'); +goog.provide('goog.testing.PerformanceTable'); + +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.testing.PerformanceTimer'); + + + +/** + * A UI widget that runs performance tests and displays the results. + * @param {Element} root The element where the table should be attached. + * @param {goog.testing.PerformanceTimer=} opt_timer A timer to use for + * executing functions and profiling them. + * @param {number=} opt_precision Number of digits of precision to include in + * results. Defaults to 0. + * @param {number=} opt_numSamples The number of samples to take. Defaults to 5. + * @constructor + * @final + */ +goog.testing.PerformanceTable = function( + root, opt_timer, opt_precision, opt_numSamples) { + /** + * Where the table should be attached. + * @private {Element} + */ + this.root_ = root; + + /** + * Number of digits of precision to include in results. + * Defaults to 0. + * @private {number} + */ + this.precision_ = opt_precision || 0; + + var timer = opt_timer; + if (!timer) { + timer = new goog.testing.PerformanceTimer(); + timer.setNumSamples(opt_numSamples || 5); + timer.setDiscardOutliers(true); + } + + /** + * A timer for running the tests. + * @private {goog.testing.PerformanceTimer} + */ + this.timer_ = timer; + + this.initRoot_(); +}; + + +/** + * @return {goog.testing.PerformanceTimer} The timer being used. + */ +goog.testing.PerformanceTable.prototype.getTimer = function() { + return this.timer_; +}; + + +/** + * Render the initial table. + * @private + */ +goog.testing.PerformanceTable.prototype.initRoot_ = function() { + this.root_.innerHTML = '' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + '
Test DescriptionRunsResults (ms)
AverageMedianStd DevMinimumMaximum
'; +}; + + +/** + * @return {Element} The body of the table. + * @private + */ +goog.testing.PerformanceTable.prototype.getTableBody_ = function() { + return goog.dom.getElementsByTagName( + goog.dom.TagName.TBODY, goog.asserts.assert(this.root_))[0]; +}; + + +/** + * Round to the specified precision. + * @param {number} num The number to round. + * @return {string} The rounded number, as a string. + * @private + */ +goog.testing.PerformanceTable.prototype.round_ = function(num) { + var factor = Math.pow(10, this.precision_); + return String(Math.round(num * factor) / factor); +}; + + +/** + * Run the given function with the performance timer, and show the results. + * @param {Function} fn The function to run. + * @param {string=} opt_desc A description to associate with this run. + */ +goog.testing.PerformanceTable.prototype.run = function(fn, opt_desc) { + this.runTask( + new goog.testing.PerformanceTimer.Task(/** @type {function()} */ (fn)), + opt_desc); +}; + + +/** + * Run the given task with the performance timer, and show the results. + * @param {goog.testing.PerformanceTimer.Task} task The performance timer task + * to run. + * @param {string=} opt_desc A description to associate with this run. + */ +goog.testing.PerformanceTable.prototype.runTask = function(task, opt_desc) { + var results = this.timer_.runTask(task); + this.recordResults(results, opt_desc); +}; + + +/** + * Record a performance timer results object to the performance table. See + * `goog.testing.PerformanceTimer` for details of the format of this + * object. + * @param {Object} results The performance timer results object. + * @param {string=} opt_desc A description to associate with these results. + */ +goog.testing.PerformanceTable.prototype.recordResults = function( + results, opt_desc) { + var average = results['average']; + var standardDeviation = results['standardDeviation']; + var isSuspicious = average < 0 || standardDeviation > average * .5; + var resultsRow = goog.dom.createDom( + goog.dom.TagName.TR, null, + goog.dom.createDom( + goog.dom.TagName.TD, 'test-description', + opt_desc || 'No description'), + goog.dom.createDom( + goog.dom.TagName.TD, 'test-count', String(results['count'])), + goog.dom.createDom( + goog.dom.TagName.TD, 'test-average', this.round_(average)), + goog.dom.createDom( + goog.dom.TagName.TD, 'test-median', String(results['median'])), + goog.dom.createDom( + goog.dom.TagName.TD, 'test-standard-deviation', + this.round_(standardDeviation)), + goog.dom.createDom( + goog.dom.TagName.TD, 'test-minimum', String(results['minimum'])), + goog.dom.createDom( + goog.dom.TagName.TD, 'test-maximum', String(results['maximum']))); + if (isSuspicious) { + resultsRow.className = 'test-suspicious'; + } + this.getTableBody_().appendChild(resultsRow); +}; + + +/** + * Report an error in the table. + * @param {*} reason The reason for the error. + */ +goog.testing.PerformanceTable.prototype.reportError = function(reason) { + this.getTableBody_().appendChild( + goog.dom.createDom( + goog.dom.TagName.TR, null, + goog.dom.createDom( + goog.dom.TagName.TD, {'class': 'test-error', 'colSpan': 5}, + String(reason)))); +}; diff --git a/closure-library/closure/goog/testing/performancetimer.js b/closure-library/closure/goog/testing/performancetimer.js new file mode 100644 index 0000000000..ee97a95f78 --- /dev/null +++ b/closure-library/closure/goog/testing/performancetimer.js @@ -0,0 +1,440 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Performance timer. + * + * {@see goog.testing.benchmark} for an easy way to use this functionality. + * + * @author attila@google.com (Attila Bodis) + */ + +goog.setTestOnly('goog.testing.PerformanceTimer'); +goog.provide('goog.testing.PerformanceTimer'); +goog.provide('goog.testing.PerformanceTimer.Task'); + +goog.require('goog.array'); +goog.require('goog.async.Deferred'); +goog.require('goog.math'); + + + +/** + * Creates a performance timer that runs test functions a number of times to + * generate timing samples, and provides performance statistics (minimum, + * maximum, average, and standard deviation). + * @param {number=} opt_numSamples Number of times to run the test function; + * defaults to 10. + * @param {number=} opt_timeoutInterval Number of milliseconds after which the + * test is to be aborted; defaults to 5 seconds (5,000ms). + * @constructor + */ +goog.testing.PerformanceTimer = function(opt_numSamples, opt_timeoutInterval) { + /** + * Number of times the test function is to be run; defaults to 10. + * @private {number} + */ + this.numSamples_ = opt_numSamples || 10; + + /** + * Number of milliseconds after which the test is to be aborted; defaults to + * 5,000ms. + * @private {number} + */ + this.timeoutInterval_ = opt_timeoutInterval || 5000; + + /** + * Whether to discard outliers (i.e. the smallest and the largest values) + * from the sample set before computing statistics. Defaults to false. + * @private {boolean} + */ + this.discardOutliers_ = false; +}; + + +/** + * A function whose subsequent calls differ in milliseconds. Used to calculate + * the start and stop checkpoint times for runs. Note that high performance + * timers do not necessarily return the current time in milliseconds. + * @return {number} + * @private + */ +goog.testing.PerformanceTimer.now_ = function() { + // goog.now is used in DEBUG mode to make the class easier to test. + return !goog.DEBUG && window.performance && window.performance.now ? + window.performance.now() : + goog.now(); +}; + + +/** + * @return {number} The number of times the test function will be run. + */ +goog.testing.PerformanceTimer.prototype.getNumSamples = function() { + return this.numSamples_; +}; + + +/** + * Sets the number of times the test function will be run. + * @param {number} numSamples Number of times to run the test function. + */ +goog.testing.PerformanceTimer.prototype.setNumSamples = function(numSamples) { + this.numSamples_ = numSamples; +}; + + +/** + * @return {number} The number of milliseconds after which the test times out. + */ +goog.testing.PerformanceTimer.prototype.getTimeoutInterval = function() { + return this.timeoutInterval_; +}; + + +/** + * Sets the number of milliseconds after which the test times out. + * @param {number} timeoutInterval Timeout interval in ms. + */ +goog.testing.PerformanceTimer.prototype.setTimeoutInterval = function( + timeoutInterval) { + this.timeoutInterval_ = timeoutInterval; +}; + + +/** + * Sets whether to ignore the smallest and the largest values when computing + * stats. + * @param {boolean} discard Whether to discard outlier values. + */ +goog.testing.PerformanceTimer.prototype.setDiscardOutliers = function(discard) { + this.discardOutliers_ = discard; +}; + + +/** + * @return {boolean} Whether outlier values are discarded prior to computing + * stats. + */ +goog.testing.PerformanceTimer.prototype.isDiscardOutliers = function() { + return this.discardOutliers_; +}; + + +/** + * Executes the test function the required number of times (or until the + * test run exceeds the timeout interval, whichever comes first). Returns + * an object containing the following: + *
+ *   {
+ *     'average': average execution time (ms)
+ *     'count': number of executions (may be fewer than expected due to timeout)
+ *     'maximum': longest execution time (ms)
+ *     'minimum': shortest execution time (ms)
+ *     'standardDeviation': sample standard deviation (ms)
+ *     'total': total execution time (ms)
+ *   }
+ * 
+ * + * @param {Function} testFn Test function whose performance is to + * be measured. + * @return {!Object} Object containing performance stats. + */ +goog.testing.PerformanceTimer.prototype.run = function(testFn) { + return this.runTask( + new goog.testing.PerformanceTimer.Task( + /** @type {goog.testing.PerformanceTimer.TestFunction} */ (testFn))); +}; + + +/** + * Executes the test function of the specified task as described in + * `run`. In addition, if specified, the set up and tear down functions of + * the task are invoked before and after each invocation of the test function. + * @see goog.testing.PerformanceTimer#run + * @param {goog.testing.PerformanceTimer.Task} task A task describing the test + * function to invoke. + * @return {!Object} Object containing performance stats. + */ +goog.testing.PerformanceTimer.prototype.runTask = function(task) { + var samples = []; + var testStart = goog.testing.PerformanceTimer.now_(); + var totalRunTime = 0; + + var testFn = task.getTest(); + var setUpFn = task.getSetUp(); + var tearDownFn = task.getTearDown(); + + for (var i = 0; i < this.numSamples_ && totalRunTime <= this.timeoutInterval_; + i++) { + setUpFn(); + var sampleStart = goog.testing.PerformanceTimer.now_(); + testFn(); + var sampleEnd = goog.testing.PerformanceTimer.now_(); + tearDownFn(); + samples[i] = sampleEnd - sampleStart; + totalRunTime = sampleEnd - testStart; + } + + return this.finishTask_(samples); +}; + + +/** + * Finishes the run of a task by creating a result object from samples, in the + * format described in `run`. + * @see goog.testing.PerformanceTimer#run + * @param {!Array} samples The samples to analyze. + * @return {!Object} Object containing performance stats. + * @private + */ +goog.testing.PerformanceTimer.prototype.finishTask_ = function(samples) { + if (this.discardOutliers_ && samples.length > 2) { + goog.array.remove(samples, Math.min.apply(null, samples)); + goog.array.remove(samples, Math.max.apply(null, samples)); + } + + return goog.testing.PerformanceTimer.createResults(samples); +}; + + +/** + * Executes the test function of the specified task asynchronously. The test + * function is expected to take a callback as input and has to call it to signal + * that it's done. In addition, if specified, the setUp and tearDown functions + * of the task are invoked before and after each invocation of the test + * function. Note that setUp/tearDown functions take a callback as input and + * must call this callback when they are done. + * @see goog.testing.PerformanceTimer#run + * @param {goog.testing.PerformanceTimer.Task} task A task describing the test + * function to invoke. + * @return {!goog.async.Deferred} The deferred result, eventually an object + * containing performance stats. + */ +goog.testing.PerformanceTimer.prototype.runAsyncTask = function(task) { + var samples = []; + var testStart = goog.testing.PerformanceTimer.now_(); + + var testFn = task.getTest(); + var setUpFn = task.getSetUp(); + var tearDownFn = task.getTearDown(); + + // Note that this uses a separate code path from runTask() because + // implementing runTask() in terms of runAsyncTask() could easily cause + // a stack overflow if there are many iterations. + var result = new goog.async.Deferred(); + this.runAsyncTaskSample_( + testFn, setUpFn, tearDownFn, result, samples, testStart); + return result; +}; + + +/** + * Runs a task once, waits for the test function to complete asynchronously + * and starts another run if not enough samples have been collected. Otherwise + * finishes this task. + * @param {goog.testing.PerformanceTimer.TestFunction} testFn The test function. + * @param {goog.testing.PerformanceTimer.TestFunction} setUpFn The set up + * function that will be called once before the test function is run. + * @param {goog.testing.PerformanceTimer.TestFunction} tearDownFn The set up + * function that will be called once after the test function completed. + * @param {!goog.async.Deferred} result The deferred result, eventually an + * object containing performance stats. + * @param {!Array} samples The time samples from all runs of the test + * function so far. + * @param {number} testStart The timestamp when the first sample was started. + * @private + */ +goog.testing.PerformanceTimer.prototype.runAsyncTaskSample_ = function( + testFn, setUpFn, tearDownFn, result, samples, testStart) { + var timer = this; + timer.handleOptionalDeferred_(setUpFn, function() { + var sampleStart = goog.testing.PerformanceTimer.now_(); + timer.handleOptionalDeferred_(testFn, function() { + var sampleEnd = goog.testing.PerformanceTimer.now_(); + timer.handleOptionalDeferred_(tearDownFn, function() { + samples.push(sampleEnd - sampleStart); + var totalRunTime = sampleEnd - testStart; + if (samples.length < timer.numSamples_ && + totalRunTime <= timer.timeoutInterval_) { + timer.runAsyncTaskSample_( + testFn, setUpFn, tearDownFn, result, samples, testStart); + } else { + result.callback(timer.finishTask_(samples)); + } + }); + }); + }); +}; + + +/** + * Return the median of the samples. + * @param {!Array} samples + * @return {number} + */ +goog.testing.PerformanceTimer.median = function(samples) { + samples.sort(function(a, b) { + return a - b; + }); + let half = Math.floor(samples.length / 2); + if (samples.length % 2) { + return samples[half]; + } else { + return (samples[half - 1] + samples[half]) / 2.0; + } +}; + + +/** + * Execute a function that optionally returns a deferred object and continue + * with the given continuation function only once the deferred object has a + * result. + * @param {goog.testing.PerformanceTimer.TestFunction} deferredFactory The + * function that optionally returns a deferred object. + * @param {function()} continuationFunction The function that should be called + * after the optional deferred has a result. + * @private + */ +goog.testing.PerformanceTimer.prototype.handleOptionalDeferred_ = function( + deferredFactory, continuationFunction) { + var deferred = deferredFactory(); + if (deferred) { + deferred.addCallback(continuationFunction); + } else { + continuationFunction(); + } +}; + + +/** + * Creates a performance timer results object by analyzing a given array of + * sample timings. + * @param {!Array} samples The samples to analyze. + * @return {!Object} Object containing performance stats. + */ +goog.testing.PerformanceTimer.createResults = function(samples) { + return { + 'average': goog.math.average.apply(null, samples), + 'count': samples.length, + 'median': goog.testing.PerformanceTimer.median(samples), + 'maximum': Math.max.apply(null, samples), + 'minimum': Math.min.apply(null, samples), + 'standardDeviation': goog.math.standardDeviation.apply(null, samples), + 'total': goog.math.sum.apply(null, samples) + }; +}; + + +/** + * A test function whose performance should be measured or a setUp/tearDown + * function. It may optionally return a deferred object. If it does so, the + * test harness will assume the function is asynchronous and it must signal + * that it's done by setting an (empty) result on the deferred object. If the + * function doesn't return anything, the test harness will assume it's + * synchronous. + * @typedef {function():(goog.async.Deferred|undefined)} + */ +goog.testing.PerformanceTimer.TestFunction; + + + +/** + * A task for the performance timer to measure. Callers can specify optional + * setUp and tearDown methods to control state before and after each run of the + * test function. + * @param {goog.testing.PerformanceTimer.TestFunction} test Test function whose + * performance is to be measured. + * @constructor + * @final + */ +goog.testing.PerformanceTimer.Task = function(test) { + /** + * The test function to time. + * @type {goog.testing.PerformanceTimer.TestFunction} + * @private + */ + this.test_ = test; +}; + + +/** + * An optional set up function to run before each invocation of the test + * function. + * @type {goog.testing.PerformanceTimer.TestFunction} + * @private + */ +goog.testing.PerformanceTimer.Task.prototype.setUp_ = goog.nullFunction; + + +/** + * An optional tear down function to run after each invocation of the test + * function. + * @type {goog.testing.PerformanceTimer.TestFunction} + * @private + */ +goog.testing.PerformanceTimer.Task.prototype.tearDown_ = goog.nullFunction; + + +/** + * @return {goog.testing.PerformanceTimer.TestFunction} The test function to + * time. + */ +goog.testing.PerformanceTimer.Task.prototype.getTest = function() { + return this.test_; +}; + + +/** + * Specifies a set up function to be invoked before each invocation of the test + * function. + * @param {goog.testing.PerformanceTimer.TestFunction} setUp The set up + * function. + * @return {!goog.testing.PerformanceTimer.Task} This task. + */ +goog.testing.PerformanceTimer.Task.prototype.withSetUp = function(setUp) { + this.setUp_ = setUp; + return this; +}; + + +/** + * @return {goog.testing.PerformanceTimer.TestFunction} The set up function or + * the default no-op function if none was specified. + */ +goog.testing.PerformanceTimer.Task.prototype.getSetUp = function() { + return this.setUp_; +}; + + +/** + * Specifies a tear down function to be invoked after each invocation of the + * test function. + * @param {goog.testing.PerformanceTimer.TestFunction} tearDown The tear down + * function. + * @return {!goog.testing.PerformanceTimer.Task} This task. + */ +goog.testing.PerformanceTimer.Task.prototype.withTearDown = function(tearDown) { + this.tearDown_ = tearDown; + return this; +}; + + +/** + * @return {goog.testing.PerformanceTimer.TestFunction} The tear down function + * or the default no-op function if none was specified. + */ +goog.testing.PerformanceTimer.Task.prototype.getTearDown = function() { + return this.tearDown_; +}; diff --git a/closure-library/closure/goog/testing/propertyreplacer.js b/closure-library/closure/goog/testing/propertyreplacer.js new file mode 100644 index 0000000000..ac720f0fb4 --- /dev/null +++ b/closure-library/closure/goog/testing/propertyreplacer.js @@ -0,0 +1,315 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Helper class for creating stubs for testing. + * + */ + +goog.setTestOnly('goog.testing.PropertyReplacer'); +goog.provide('goog.testing.PropertyReplacer'); + +goog.require('goog.asserts'); +goog.require('goog.userAgent'); + + + +/** + * Helper class for stubbing out variables and object properties for unit tests. + * This class can change the value of some variables before running the test + * cases, and to reset them in the tearDown phase. + * See googletest.StubOutForTesting as an analogy in Python: + * http://protobuf.googlecode.com/svn/trunk/python/stubout.py + * + * Example usage: + * + * var stubs = new goog.testing.PropertyReplacer(); + * + * function setUp() { + * // Mock functions used in all test cases. + * stubs.replace(Math, 'random', function() { + * return 4; // Chosen by fair dice roll. Guaranteed to be random. + * }); + * } + * + * function tearDown() { + * stubs.reset(); + * } + * + * function testThreeDice() { + * // Mock a constant used only in this test case. + * stubs.set(goog.global, 'DICE_COUNT', 3); + * assertEquals(12, rollAllDice()); + * } + * + * Constraints on altered objects: + *
    + *
  • DOM subclasses aren't supported. + *
  • The value of the objects' constructor property must either be equal to + * the real constructor or kept untouched. + *
+ * + * Code compiled with property renaming may need to use + * `goog.reflect.objectProperty` instead of simply naming the property to + * replace. + * + * @constructor + * @final + */ +goog.testing.PropertyReplacer = function() { + /** + * Stores the values changed by the set() method in chronological order. + * Its items are objects with 3 fields: 'object', 'key', 'value'. The + * original value for the given key in the given object is stored under the + * 'value' key. + * @type {Array<{ object: ?, key: string, value: ? }>} + * @private + */ + this.original_ = []; +}; + + +/** + * Indicates that a key didn't exist before having been set by the set() method. + * @private @const + */ +goog.testing.PropertyReplacer.NO_SUCH_KEY_ = {}; + + +/** + * Tells if the given key exists in the object. Ignores inherited fields. + * @param {!Object|!Function} obj The JavaScript or native object or function + * whose key is to be checked. + * @param {string} key The key to check. + * @return {boolean} Whether the object has the key as own key. + * @private + * @suppress {unusedLocalVariables} + */ +goog.testing.PropertyReplacer.hasKey_ = function(obj, key) { + if (!(key in obj)) { + return false; + } + // hasOwnProperty is only reliable with JavaScript objects. It returns false + // for built-in DOM attributes. + if (Object.prototype.hasOwnProperty.call(obj, key)) { + return true; + } + // In all browsers except Opera obj.constructor never equals to Object if + // obj is an instance of a native class. In Opera we have to fall back on + // examining obj.toString(). + if (obj.constructor == Object && + (!goog.userAgent.OPERA || + Object.prototype.toString.call(obj) == '[object Object]')) { + return false; + } + try { + // Firefox hack to consider "className" part of the HTML elements or + // "body" part of document. Although they are defined in the prototype of + // HTMLElement or Document, accessing them this way throws an exception. + //
+    //   var dummy = document.body.constructor.prototype.className
+    //   [Exception... "Cannot modify properties of a WrappedNative"]
+    // 
+ var dummy = obj.constructor.prototype[key]; + } catch (e) { + return true; + } + return !(key in obj.constructor.prototype); +}; + + +/** + * Deletes a key from an object. Sets it to undefined or empty string if the + * delete failed. + * @param {!Object|!Function} obj The object or function to delete a key from. + * @param {string} key The key to delete. + * @throws {Error} In case of trying to set a read-only property + * @private + */ +goog.testing.PropertyReplacer.deleteKey_ = function(obj, key) { + try { + delete obj[key]; + // Delete has no effect for built-in properties of DOM nodes in FF. + if (!goog.testing.PropertyReplacer.hasKey_(obj, key)) { + return; + } + } catch (e) { + // IE throws TypeError when trying to delete properties of native objects + // (e.g. DOM nodes or window), even if they have been added by JavaScript. + } + + obj[key] = undefined; + if (obj[key] == 'undefined') { + // Some properties such as className in IE are always evaluated as string + // so undefined will become 'undefined'. + obj[key] = ''; + } + + if (obj[key]) { + throw new Error( + 'Cannot delete non configurable property "' + key + '" in ' + obj); + } +}; + + +/** + * Restore the original state of a key in an object. + * @param {{ object: ?, key: string, value: ? }} original Original state + * @private + */ +goog.testing.PropertyReplacer.restoreOriginal_ = function(original) { + if (original.value == goog.testing.PropertyReplacer.NO_SUCH_KEY_) { + goog.testing.PropertyReplacer.deleteKey_(original.object, original.key); + } else { + original.object[original.key] = original.value; + } +}; + + +/** + * Adds or changes a value in an object while saving its original state. + * @param {Object|Function} obj The JavaScript or native object or function to + * alter. See the constraints in the class description. + * @param {string} key The key to change the value for. + * @param {*} value The new value to set. + * @throws {Error} In case of trying to set a read-only property. + */ +goog.testing.PropertyReplacer.prototype.set = function(obj, key, value) { + goog.asserts.assert(obj); + var origValue = goog.testing.PropertyReplacer.hasKey_(obj, key) ? + obj[key] : + goog.testing.PropertyReplacer.NO_SUCH_KEY_; + this.original_.push({object: obj, key: key, value: origValue}); + obj[key] = value; + + // Check whether obj[key] was a read-only value and the assignment failed. + // Also, check that we're not comparing returned pixel values when "value" + // is 0. In other words, account for this case: + // document.body.style.margin = 0; + // document.body.style.margin; // returns "0px" + if (obj[key] != value && (value + 'px') != obj[key]) { + throw new Error( + 'Cannot overwrite read-only property "' + key + '" in ' + obj); + } +}; + + +/** + * Changes an existing value in an object to another one of the same type while + * saving its original state. The advantage of `replace` over {@link #set} + * is that `replace` protects against typos and erroneously passing tests + * after some members have been renamed during a refactoring. + * @param {Object|Function} obj The JavaScript or native object or function to + * alter. See the constraints in the class description. + * @param {string} key The key to change the value for. It has to be present + * either in `obj` or in its prototype chain. + * @param {*} value The new value to set. + * @param {boolean=} opt_allowNullOrUndefined By default, this method requires + * `value` to match the type of the existing value, as determined by + * {@link goog.typeOf}. Setting opt_allowNullOrUndefined to `true` + * allows an existing value to be replaced by `null` or + `undefined`, or vice versa. + * @throws {Error} In case of missing key or type mismatch. + */ +goog.testing.PropertyReplacer.prototype.replace = function( + obj, key, value, opt_allowNullOrUndefined) { + if (!(key in obj)) { + throw new Error('Cannot replace missing property "' + key + '" in ' + obj); + } + // If opt_allowNullOrUndefined is true, then we do not check the types if + // either the original or new value is null or undefined. + var shouldCheckTypes = !opt_allowNullOrUndefined || + (goog.isDefAndNotNull(obj[key]) && goog.isDefAndNotNull(value)); + if (shouldCheckTypes) { + var originalType = goog.typeOf(obj[key]); + var newType = goog.typeOf(value); + if (originalType != newType) { + throw new Error( + 'Cannot replace property "' + key + '" in ' + obj + + ' with a value of different type (expected ' + originalType + + ', found ' + newType + ')'); + } + } + this.set(obj, key, value); +}; + + +/** + * Builds an object structure for the provided namespace path. Doesn't + * overwrite those prefixes of the path that are already objects or functions. + * @param {string} path The path to create or alter, e.g. 'goog.ui.Menu'. + * @param {*} value The value to set. + */ +goog.testing.PropertyReplacer.prototype.setPath = function(path, value) { + var parts = path.split('.'); + var obj = goog.global; + for (var i = 0; i < parts.length - 1; i++) { + var part = parts[i]; + if (part == 'prototype' && !obj[part]) { + throw new Error( + 'Cannot set the prototype of ' + parts.slice(0, i).join('.')); + } + if (!goog.isObject(obj[part]) && !goog.isFunction(obj[part])) { + this.set(obj, part, {}); + } + obj = obj[part]; + } + this.set(obj, parts[parts.length - 1], value); +}; + + +/** + * Deletes the key from the object while saving its original value. + * @param {Object|Function} obj The JavaScript or native object or function to + * alter. See the constraints in the class description. + * @param {string} key The key to delete. + */ +goog.testing.PropertyReplacer.prototype.remove = function(obj, key) { + if (obj && goog.testing.PropertyReplacer.hasKey_(obj, key)) { + this.original_.push({object: obj, key: key, value: obj[key]}); + goog.testing.PropertyReplacer.deleteKey_(obj, key); + } +}; + + +/** + * Restore the original state of key in an object. + * @param {!Object|!Function} obj The JavaScript or native object whose state + * should be restored. + * @param {string} key The key to restore the original value for. + * @throws {Error} In case the object/key pair hadn't been modified earlier. + */ +goog.testing.PropertyReplacer.prototype.restore = function(obj, key) { + for (var i = this.original_.length - 1; i >= 0; i--) { + var original = this.original_[i]; + if (original.object === obj && original.key == key) { + goog.testing.PropertyReplacer.restoreOriginal_(original); + this.original_.splice(i, 1); + return; + } + } + throw new Error('Cannot restore unmodified property "' + key + '" of ' + obj); +}; + + +/** + * Resets all changes made by goog.testing.PropertyReplacer.prototype.set. + */ +goog.testing.PropertyReplacer.prototype.reset = function() { + for (var i = this.original_.length - 1; i >= 0; i--) { + goog.testing.PropertyReplacer.restoreOriginal_(this.original_[i]); + delete this.original_[i]; + } + this.original_.length = 0; +}; diff --git a/closure-library/closure/goog/testing/proto2/proto2.js b/closure-library/closure/goog/testing/proto2/proto2.js new file mode 100644 index 0000000000..a0dde25d26 --- /dev/null +++ b/closure-library/closure/goog/testing/proto2/proto2.js @@ -0,0 +1,147 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Test helpers to compare goog.proto2.Messages. + * + */ + +goog.setTestOnly('goog.testing.proto2'); +goog.provide('goog.testing.proto2'); + +goog.require('goog.proto2.Message'); +goog.require('goog.proto2.ObjectSerializer'); +goog.require('goog.testing.asserts'); + + +/** + * Compares two goog.proto2.Message instances of the same type. + * @param {!goog.proto2.Message} expected First message. + * @param {!goog.proto2.Message} actual Second message. + * @param {string} path Path to the messages. + * @return {string} A string describing where they differ. Empty string if they + * are equal. + * @private + */ +goog.testing.proto2.findDifferences_ = function(expected, actual, path) { + var fields = expected.getDescriptor().getFields(); + for (var i = 0; i < fields.length; i++) { + var field = fields[i]; + var newPath = (path ? path + '/' : '') + field.getName(); + + if (expected.has(field) && !actual.has(field)) { + return newPath + ' should be present'; + } + if (!expected.has(field) && actual.has(field)) { + return newPath + ' should not be present'; + } + + if (expected.has(field)) { + var isComposite = field.isCompositeType(); + + if (field.isRepeated()) { + var expectedCount = expected.countOf(field); + var actualCount = actual.countOf(field); + if (expectedCount != actualCount) { + return newPath + ' should have ' + expectedCount + ' items, ' + + 'but has ' + actualCount; + } + + for (var j = 0; j < expectedCount; j++) { + var expectedItem = expected.get(field, j); + var actualItem = actual.get(field, j); + if (isComposite) { + var itemDiff = goog.testing.proto2.findDifferences_( + /** @type {!goog.proto2.Message} */ (expectedItem), + /** @type {!goog.proto2.Message} */ (actualItem), + newPath + '[' + j + ']'); + if (itemDiff) { + return itemDiff; + } + } else { + if (expectedItem != actualItem) { + return newPath + '[' + j + '] should be ' + expectedItem + + ', but was ' + actualItem; + } + } + } + } else { + var expectedValue = expected.get(field); + var actualValue = actual.get(field); + if (isComposite) { + var diff = goog.testing.proto2.findDifferences_( + /** @type {!goog.proto2.Message} */ (expectedValue), + /** @type {!goog.proto2.Message} */ (actualValue), newPath); + if (diff) { + return diff; + } + } else { + if (expectedValue != actualValue) { + return newPath + ' should be ' + expectedValue + ', but was ' + + actualValue; + } + } + } + } + } + + return ''; +}; + + +/** + * Compares two goog.proto2.Message objects. Gives more readable output than + * assertObjectEquals on mismatch. + * @param {!goog.proto2.Message} expected Expected proto2 message. + * @param {!goog.proto2.Message} actual Actual proto2 message. + * @param {string=} opt_failureMessage Failure message when the values don't + * match. + */ +goog.testing.proto2.assertEquals = function( + expected, actual, opt_failureMessage) { + var failureSummary = opt_failureMessage || ''; + if (!(expected instanceof goog.proto2.Message) || + !(actual instanceof goog.proto2.Message)) { + goog.testing.asserts.raiseException( + failureSummary, + 'Bad arguments were passed to goog.testing.proto2.assertEquals'); + } + if (expected.constructor != actual.constructor) { + goog.testing.asserts.raiseException( + failureSummary, 'Message type mismatch: ' + + expected.getDescriptor().getFullName() + ' != ' + + actual.getDescriptor().getFullName()); + } + var diff = goog.testing.proto2.findDifferences_(expected, actual, ''); + if (diff) { + goog.testing.asserts.raiseException(failureSummary, diff); + } +}; + + +/** + * Helper function to quickly build protocol buffer messages from JSON objects. + * @param {function(new:MessageType)} messageCtor A constructor that + * creates a `goog.proto2.Message` subclass instance. + * @param {!Object} json JSON object which uses field names as keys. + * @return {MessageType} The deserialized protocol buffer. + * @template MessageType + */ +goog.testing.proto2.fromObject = function(messageCtor, json) { + var serializer = new goog.proto2.ObjectSerializer( + goog.proto2.ObjectSerializer.KeyOption.NAME); + var message = new messageCtor; + serializer.deserializeTo(message, json); + return message; +}; diff --git a/closure-library/closure/goog/testing/pseudorandom.js b/closure-library/closure/goog/testing/pseudorandom.js new file mode 100644 index 0000000000..5b0010118f --- /dev/null +++ b/closure-library/closure/goog/testing/pseudorandom.js @@ -0,0 +1,181 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// 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 PseudoRandom provides a mechanism for generating deterministic + * pseudo random numbers based on a seed. Based on the Park-Miller algorithm. + * See https://doi.org/10.1145%2F63039.63042 for details. + * + */ + +goog.setTestOnly('goog.testing.PseudoRandom'); +goog.provide('goog.testing.PseudoRandom'); + +goog.require('goog.Disposable'); + + + +/** + * Class for unit testing code that uses Math.random. Generates deterministic + * random numbers. + * + * @param {number=} opt_seed The seed to use. + * @param {boolean=} opt_install Whether to install the PseudoRandom at + * construction time. + * @extends {goog.Disposable} + * @constructor + * @final + */ +goog.testing.PseudoRandom = function(opt_seed, opt_install) { + goog.Disposable.call(this); + + if (!goog.isDef(opt_seed)) { + opt_seed = goog.testing.PseudoRandom.seedUniquifier_++ + goog.now(); + } + this.seed(opt_seed); + + if (opt_install) { + this.install(); + } +}; +goog.inherits(goog.testing.PseudoRandom, goog.Disposable); + + +/** + * Helps create a unique seed. + * @type {number} + * @private + */ +goog.testing.PseudoRandom.seedUniquifier_ = 0; + + +/** + * Constant used as part of the algorithm. + * @type {number} + */ +goog.testing.PseudoRandom.A = 48271; + + +/** + * Constant used as part of the algorithm. 2^31 - 1. + * @type {number} + */ +goog.testing.PseudoRandom.M = 2147483647; + + +/** + * Constant used as part of the algorithm. It is equal to M / A. + * @type {number} + */ +goog.testing.PseudoRandom.Q = 44488; + + +/** + * Constant used as part of the algorithm. It is equal to M % A. + * @type {number} + */ +goog.testing.PseudoRandom.R = 3399; + + +/** + * Constant used as part of the algorithm to get values from range [0, 1). + * @type {number} + */ +goog.testing.PseudoRandom.ONE_OVER_M_MINUS_ONE = + 1.0 / (goog.testing.PseudoRandom.M - 1); + + +/** + * The seed of the random sequence and also the next returned value (before + * normalization). Must be between 1 and M - 1 (inclusive). + * @type {number} + * @private + */ +goog.testing.PseudoRandom.prototype.seed_ = 1; + + +/** + * Whether this PseudoRandom has been installed. + * @type {boolean} + * @private + */ +goog.testing.PseudoRandom.prototype.installed_; + + +/** + * The original Math.random function. + * @type {function(): number} + * @private + */ +goog.testing.PseudoRandom.prototype.mathRandom_; + + +/** + * Installs this PseudoRandom as the system number generator. + */ +goog.testing.PseudoRandom.prototype.install = function() { + if (!this.installed_) { + this.mathRandom_ = Math.random; + Math.random = goog.bind(this.random, this); + this.installed_ = true; + } +}; + + +/** @override */ +goog.testing.PseudoRandom.prototype.disposeInternal = function() { + goog.testing.PseudoRandom.superClass_.disposeInternal.call(this); + this.uninstall(); +}; + + +/** + * Uninstalls the PseudoRandom. + */ +goog.testing.PseudoRandom.prototype.uninstall = function() { + if (this.installed_) { + Math.random = this.mathRandom_; + this.installed_ = false; + } +}; + + +/** + * Seed the generator. + * + * @param {number=} opt_seed The seed to use. + */ +goog.testing.PseudoRandom.prototype.seed = function(opt_seed) { + this.seed_ = (opt_seed || 0) % (goog.testing.PseudoRandom.M - 1); + if (this.seed_ <= 0) { + this.seed_ += goog.testing.PseudoRandom.M - 1; + } +}; + + +/** + * @return {number} The next number in the sequence. + */ +goog.testing.PseudoRandom.prototype.random = function() { + var hi = Math.floor(this.seed_ / goog.testing.PseudoRandom.Q); + var lo = this.seed_ % goog.testing.PseudoRandom.Q; + var test = + goog.testing.PseudoRandom.A * lo - goog.testing.PseudoRandom.R * hi; + if (test > 0) { + this.seed_ = test; + } else { + this.seed_ = test + goog.testing.PseudoRandom.M; + } + return (this.seed_ - 1) * goog.testing.PseudoRandom.ONE_OVER_M_MINUS_ONE; +}; diff --git a/closure-library/closure/goog/testing/recordfunction.js b/closure-library/closure/goog/testing/recordfunction.js new file mode 100644 index 0000000000..33d77b3599 --- /dev/null +++ b/closure-library/closure/goog/testing/recordfunction.js @@ -0,0 +1,305 @@ +// Copyright 2010 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Helper class for recording the calls of a function. + * + * Example: + *
+ * var stubs = new goog.testing.PropertyReplacer();
+ *
+ * function tearDown() {
+ *   stubs.reset();
+ * }
+ *
+ * function testShuffle() {
+ *   stubs.replace(Math, 'random', goog.testing.recordFunction(Math.random));
+ *   var arr = shuffle([1, 2, 3, 4, 5]);
+ *   assertSameElements([1, 2, 3, 4, 5], arr);
+ *   assertEquals(4, Math.random.getCallCount());
+ * }
+ *
+ * function testOpenDialog() {
+ *   stubs.replace(goog.ui, 'Dialog',
+ *       goog.testing.recordConstructor(goog.ui.Dialog));
+ *   openConfirmDialog();
+ *   var lastDialogInstance = goog.ui.Dialog.getLastCall().getThis();
+ *   assertEquals('confirm', lastDialogInstance.getTitle());
+ * }
+ * 
+ * + */ + +goog.setTestOnly('goog.testing.FunctionCall'); +goog.provide('goog.testing.FunctionCall'); +goog.provide('goog.testing.recordConstructor'); +goog.provide('goog.testing.recordFunction'); + +goog.require('goog.Promise'); +goog.require('goog.promise.Resolver'); +goog.require('goog.testing.asserts'); + + +/** + * A function that represents the return type of recordFunction. + * @private + * @param {...?} var_args + * @return {?} + */ +goog.testing.recordedFunction_ = function(var_args) {}; + +/** + * @return {number} Total number of calls. + */ +goog.testing.recordedFunction_.getCallCount = function() {}; + +/** + * Asserts that the function was called a certain number of times. + * @param {number|string} a The expected number of calls (1 arg) or debug + * message (2 args). + * @param {number=} opt_b The expected number of calls (2 args only). + */ +goog.testing.recordedFunction_.assertCallCount = function(a, opt_b) {}; + +/** + * @return {!Array} All calls of the recorded + * function. + */ +goog.testing.recordedFunction_.getCalls = function() {}; + +/** + * @return {?goog.testing.FunctionCall} Last call of the recorded function or + * null if it hasn't been called. + */ +goog.testing.recordedFunction_.getLastCall = function() {}; + +/** + * Returns and removes the last call of the recorded function. + * @return {?goog.testing.FunctionCall} Last call of the recorded function or + * null if it hasn't been called. + */ +goog.testing.recordedFunction_.popLastCall = function() {}; + +/** + * Returns a goog.Promise that resolves when the recorded function has equal + * to or greater than the number of calls. + * @param {number} num + * @return {!goog.Promise} + */ +goog.testing.recordedFunction_.waitForCalls = function(num) {}; + +/** + * Resets the recorded function and removes all calls. + * @return {void} + */ +goog.testing.recordedFunction_.reset = function() {}; + +/** + * Wraps the function into another one which calls the inner function and + * records its calls. The recorded function will have 3 static methods: + * `getCallCount`, `getCalls` and `getLastCall` but won't + * inherit the original function's prototype and static fields. + * + * @param {!Function=} opt_f The function to wrap and record. Defaults to + * {@link goog.nullFunction}. + * @return {!goog.testing.recordFunction.Type} The wrapped function. + */ +goog.testing.recordFunction = function(opt_f) { + var f = opt_f || goog.nullFunction; + var calls = []; + /** @type {?goog.promise.Resolver} */ + var waitForCallsResolver = null; + /** @type {number} */ + var waitForCallsCount = 0; + + function maybeResolveWaitForCalls() { + if (waitForCallsResolver && calls.length >= waitForCallsCount) { + waitForCallsResolver.resolve(); + waitForCallsResolver = null; + waitForCallsCount = 0; + } + } + + /** @type {!goog.testing.recordFunction.Type} */ + function recordedFunction() { + var owner = /** @type {?} */ (this); + try { + var ret = f.apply(owner, arguments); + calls.push(new goog.testing.FunctionCall(f, owner, arguments, ret, null)); + maybeResolveWaitForCalls(); + return ret; + } catch (err) { + calls.push( + new goog.testing.FunctionCall(f, owner, arguments, undefined, err)); + maybeResolveWaitForCalls(); + throw err; + } + } + + /** + * @return {number} Total number of calls. + */ + recordedFunction.getCallCount = function() { return calls.length; }; + + /** + * Asserts that the function was called a certain number of times. + * @param {number|string} a The expected number of calls (1 arg) or debug + * message (2 args). + * @param {number=} opt_b The expected number of calls (2 args only). + */ + recordedFunction.assertCallCount = function(a, opt_b) { + var actual = calls.length; + var expected = arguments.length == 1 ? a : opt_b; + var message = arguments.length == 1 ? '' : ' ' + a; + assertEquals( + 'Expected ' + expected + ' call(s), but was ' + actual + '.' + message, + expected, actual); + }; + + /** + * @return {!Array} All calls of the recorded + * function. + */ + recordedFunction.getCalls = function() { return calls; }; + + + /** + * @return {goog.testing.FunctionCall} Last call of the recorded function or + * null if it hasn't been called. + */ + recordedFunction.getLastCall = function() { + return calls[calls.length - 1] || null; + }; + + /** + * Returns and removes the last call of the recorded function. + * @return {goog.testing.FunctionCall} Last call of the recorded function or + * null if it hasn't been called. + */ + recordedFunction.popLastCall = function() { return calls.pop() || null; }; + + /** + * Returns a goog.Promise that resolves when the recorded function has equal + * to or greater than the number of calls. + * @param {number} num + * @return {!goog.Promise} + */ + recordedFunction.waitForCalls = function(num) { + waitForCallsCount = num; + waitForCallsResolver = goog.Promise.withResolver(); + var promise = waitForCallsResolver.promise; + maybeResolveWaitForCalls(); + return promise; + }; + + /** + * Resets the recorded function and removes all calls. + */ + recordedFunction.reset = function() { + calls.length = 0; + waitForCallsResolver = null; + waitForCallsCount = 0; + }; + + return recordedFunction; +}; + +/** @typedef {typeof goog.testing.recordedFunction_} */ +goog.testing.recordFunction.Type; + + +/** + * Same as {@link goog.testing.recordFunction} but the recorded function will + * have the same prototype and static fields as the original one. It can be + * used with constructors. + * + * @param {!Function} ctor The function to wrap and record. + * @return {!Function} The wrapped function. + */ +goog.testing.recordConstructor = function(ctor) { + var recordedConstructor = goog.testing.recordFunction(ctor); + recordedConstructor.prototype = ctor.prototype; + goog.mixin(recordedConstructor, ctor); + return recordedConstructor; +}; + + + +/** + * Struct for a single function call. + * @param {!Function} func The called function. + * @param {!Object} thisContext `this` context of called function. + * @param {!Arguments} args Arguments of the called function. + * @param {*} ret Return value of the function or undefined in case of error. + * @param {*} error The error thrown by the function or null if none. + * @constructor + */ +goog.testing.FunctionCall = function(func, thisContext, args, ret, error) { + this.function_ = func; + this.thisContext_ = thisContext; + this.arguments_ = Array.prototype.slice.call(args); + this.returnValue_ = ret; + this.error_ = error; +}; + + +/** + * @return {!Function} The called function. + */ +goog.testing.FunctionCall.prototype.getFunction = function() { + return this.function_; +}; + + +/** + * @return {!Object} `this` context of called function. It is the same as + * the created object if the function is a constructor. + */ +goog.testing.FunctionCall.prototype.getThis = function() { + return this.thisContext_; +}; + + +/** + * @return {!Array} Arguments of the called function. + */ +goog.testing.FunctionCall.prototype.getArguments = function() { + return this.arguments_; +}; + + +/** + * Returns the nth argument of the called function. + * @param {number} index 0-based index of the argument. + * @return {*} The argument value or undefined if there is no such argument. + */ +goog.testing.FunctionCall.prototype.getArgument = function(index) { + return this.arguments_[index]; +}; + + +/** + * @return {*} Return value of the function or undefined in case of error. + */ +goog.testing.FunctionCall.prototype.getReturnValue = function() { + return this.returnValue_; +}; + + +/** + * @return {*} The error thrown by the function or null if none. + */ +goog.testing.FunctionCall.prototype.getError = function() { + return this.error_; +}; diff --git a/closure-library/closure/goog/testing/shardingtestcase.js b/closure-library/closure/goog/testing/shardingtestcase.js new file mode 100644 index 0000000000..1503db9ce3 --- /dev/null +++ b/closure-library/closure/goog/testing/shardingtestcase.js @@ -0,0 +1,127 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utility for sharding tests. + * + * Usage instructions: + *
    + *
  1. Instead of writing your large test in foo_test.html, write it in + * foo_test_template.html
  2. + *
  3. Add a call to `goog.testing.ShardingTestCase.shardByFileName()` + * near the top of your test, before any test cases or setup methods.
  4. + *
  5. Symlink foo_test_template.html into different sharded test files + * named foo_1of4_test.html, foo_2of4_test.html, etc, using `ln -s`.
  6. + *
  7. Add the symlinks as foo_1of4_test.html. + * In perforce, run the command `g4 add foo_1of4_test.html` followed + * by `g4 reopen -t symlink foo_1of4_test.html` so that perforce treats the file + * as a symlink + *
  8. + *
+ * + * @author nicksantos@google.com (Nick Santos) + */ + +goog.setTestOnly('goog.testing.ShardingTestCase'); +goog.provide('goog.testing.ShardingTestCase'); + +goog.require('goog.asserts'); +goog.require('goog.testing.TestCase'); + + + +/** + * A test case that runs tests in per-file shards. + * @param {number} shardIndex Shard index for this page, + * 1-indexed. + * @param {number} numShards Number of shards to split up test cases into. + * @param {string=} opt_name The name of the test case. + * @extends {goog.testing.TestCase} + * @constructor + * @final + */ +goog.testing.ShardingTestCase = function(shardIndex, numShards, opt_name) { + goog.testing.ShardingTestCase.base(this, 'constructor', opt_name); + + goog.asserts.assert(shardIndex > 0, 'Shard index should be positive'); + goog.asserts.assert(numShards > 0, 'Number of shards should be positive'); + goog.asserts.assert(shardIndex <= numShards, 'Shard index out of bounds'); + + /** + * @type {number} + * @private + */ + this.shardIndex_ = shardIndex; + + /** + * @type {number} + * @private + */ + this.numShards_ = numShards; +}; +goog.inherits(goog.testing.ShardingTestCase, goog.testing.TestCase); + + +/** + * Whether we've actually partitioned the tests yet. We may execute twice + * ('Run again without reloading') without failing. + * @type {boolean} + * @private + */ +goog.testing.ShardingTestCase.prototype.sharded_ = false; + + +/** + * Installs a runTests global function that goog.testing.JsUnit will use to + * run tests, which will run a single shard of the tests present on the page. + * @override + */ +goog.testing.ShardingTestCase.prototype.runTests = function() { + if (!this.sharded_) { + var numTests = this.getCount(); + goog.asserts.assert( + numTests >= this.numShards_, + 'Must have at least as many tests as shards!'); + var shardSize = Math.ceil(numTests / this.numShards_); + var startIndex = (this.shardIndex_ - 1) * shardSize; + var endIndex = startIndex + shardSize; + goog.asserts.assert( + this.order == goog.testing.TestCase.Order.SORTED, + 'Only SORTED order is allowed for sharded tests'); + this.setTests(this.getTests().slice(startIndex, endIndex)); + this.sharded_ = true; + } + + // Call original runTests method to execute the tests. + goog.testing.ShardingTestCase.base(this, 'runTests'); +}; + + +/** + * Shards tests based on the test filename. Assumes that the filename is + * formatted like 'foo_1of5_test.html'. + * @param {string=} opt_name A descriptive name for the test case. + */ +goog.testing.ShardingTestCase.shardByFileName = function(opt_name) { + var path = window.location.pathname; + var shardMatch = path.match(/_(\d+)of(\d+)_test\.(js|html)/); + goog.asserts.assert( + shardMatch, 'Filename must be of the form "foo_1of5_test.{js,html}"'); + var shardIndex = parseInt(shardMatch[1], 10); + var numShards = parseInt(shardMatch[2], 10); + + var testCase = + new goog.testing.ShardingTestCase(shardIndex, numShards, opt_name); + goog.testing.TestCase.initializeTestRunner(testCase); +}; diff --git a/closure-library/closure/goog/testing/singleton.js b/closure-library/closure/goog/testing/singleton.js new file mode 100644 index 0000000000..48de5623ea --- /dev/null +++ b/closure-library/closure/goog/testing/singleton.js @@ -0,0 +1,47 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview This module simplifies testing code which uses stateful + * singletons. `goog.testing.singleton.reset` resets all instances, so + * next time when `getInstance` is called, a new instance is created. + * It's recommended to reset the singletons in `tearDown` to prevent + * interference between subsequent tests. + * + * The `goog.testing.singleton` functions expect that the goog.DEBUG flag + * is enabled, and the tests are either uncompiled or compiled without renaming. + * + */ + +goog.setTestOnly('goog.testing.singleton'); +goog.provide('goog.testing.singleton'); + + +/** + * Deletes all singleton instances, so `getInstance` will return a new + * instance on next call. + */ +goog.testing.singleton.reset = function() { + var singletons = goog.getObjectByName('goog.instantiatedSingletons_'); + var ctor; + while (ctor = singletons.pop()) { + delete ctor.instance_; + } +}; + + +/** + * @deprecated Please use `goog.addSingletonGetter`. + */ +goog.testing.singleton.addSingletonGetter = goog.addSingletonGetter; diff --git a/closure-library/closure/goog/testing/stacktrace.js b/closure-library/closure/goog/testing/stacktrace.js new file mode 100644 index 0000000000..b43b5935c2 --- /dev/null +++ b/closure-library/closure/goog/testing/stacktrace.js @@ -0,0 +1,555 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Tools for parsing and pretty printing error stack traces. + * + */ + +goog.setTestOnly('goog.testing.stacktrace'); +goog.provide('goog.testing.stacktrace'); +goog.provide('goog.testing.stacktrace.Frame'); + + + +/** + * Class representing one stack frame. + * @param {string} context Context object, empty in case of global functions or + * if the browser doesn't provide this information. + * @param {string} name Function name, empty in case of anonymous functions. + * @param {string} alias 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() {};. + * @param {string} path File path or URL including line number and optionally + * column number separated by colons. + * @constructor + * @final + */ +goog.testing.stacktrace.Frame = function(context, name, alias, path) { + this.context_ = context; + this.name_ = name; + this.alias_ = alias; + this.path_ = path; +}; + + +/** + * @return {string} The function name or empty string if the function is + * anonymous and the object field which it's assigned to is unknown. + */ +goog.testing.stacktrace.Frame.prototype.getName = function() { + return this.name_; +}; + + +/** + * @return {boolean} Whether the stack frame contains an anonymous function. + */ +goog.testing.stacktrace.Frame.prototype.isAnonymous = function() { + return !this.name_ || this.context_ == '[object Object]'; +}; + + +/** + * Brings one frame of the stack trace into a common format across browsers. + * @return {string} Pretty printed stack frame. + */ +goog.testing.stacktrace.Frame.prototype.toCanonicalString = function() { + var htmlEscape = goog.testing.stacktrace.htmlEscape_; + var deobfuscate = goog.testing.stacktrace.maybeDeobfuscateFunctionName_; + + var canonical = [ + this.context_ ? htmlEscape(this.context_) + '.' : '', + this.name_ ? htmlEscape(deobfuscate(this.name_)) : 'anonymous', + this.alias_ ? ' [as ' + htmlEscape(deobfuscate(this.alias_)) + ']' : '' + ]; + + if (this.path_) { + canonical.push(' at '); + canonical.push(htmlEscape(this.path_)); + } + return canonical.join(''); +}; + + +/** + * Maximum number of steps while the call chain is followed. + * @private {number} + * @const + */ +goog.testing.stacktrace.MAX_DEPTH_ = 20; + + +/** + * 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. + * @private {number} + * @const + */ +goog.testing.stacktrace.MAX_FIREFOX_FRAMESTRING_LENGTH_ = 500000; + + +/** + * RegExp pattern for JavaScript identifiers. We don't support Unicode + * identifiers defined in ECMAScript v3. + * @private {string} + * @const + */ +goog.testing.stacktrace.IDENTIFIER_PATTERN_ = '[a-zA-Z_$][\\w$]*'; + + +/** + * RegExp pattern for function name alias in the V8 stack trace. + * @private {string} + * @const + */ +goog.testing.stacktrace.V8_ALIAS_PATTERN_ = + '(?: \\[as (' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + ')\\])?'; + + +/** + * RegExp pattern for the context of a function call in a V8 stack trace. + * Creates an optional submatch for the namespace identifier including the + * "new" keyword for constructor calls (e.g. "new foo.Bar"). + * @private {string} + * @const + */ +goog.testing.stacktrace.V8_CONTEXT_PATTERN_ = + '(?:((?:new )?(?:\\[object Object\\]|' + + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + '(?:\\.' + + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + ')*))\\.)?'; + + +/** + * RegExp pattern for function names and constructor calls in the V8 stack + * trace. + * @private {string} + * @const + */ +goog.testing.stacktrace.V8_FUNCTION_NAME_PATTERN_ = '(?:new )?(?:' + + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + '|)'; + + +/** + * RegExp pattern for function call in the V8 stack trace. Creates 3 submatches + * with context object (optional), function name and function alias (optional). + * @private {string} + * @const + */ +goog.testing.stacktrace.V8_FUNCTION_CALL_PATTERN_ = ' ' + + goog.testing.stacktrace.V8_CONTEXT_PATTERN_ + '(' + + goog.testing.stacktrace.V8_FUNCTION_NAME_PATTERN_ + ')' + + goog.testing.stacktrace.V8_ALIAS_PATTERN_; + + +/** + * RegExp pattern for an URL + position inside the file. + * @private {string} + * @const + */ +goog.testing.stacktrace.URL_PATTERN_ = + '((?:http|https|file)://[^\\s)]+|javascript:.*)'; + + +/** + * RegExp pattern for an URL + line number + column number in V8. + * The URL is either in submatch 1 or submatch 2. + * @private {string} + * @const + */ +goog.testing.stacktrace.CHROME_URL_PATTERN_ = ' (?:' + + '\\(unknown source\\)' + + '|' + + '\\(native\\)' + + '|' + + '\\((.+)\\)|(.+))'; + + +/** + * Regular expression for parsing one stack frame in V8. For more information + * on V8 stack frame formats, see + * https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi. + * @private {!RegExp} + * @const + */ +goog.testing.stacktrace.V8_STACK_FRAME_REGEXP_ = new RegExp( + '^ at' + + '(?:' + goog.testing.stacktrace.V8_FUNCTION_CALL_PATTERN_ + ')?' + + goog.testing.stacktrace.CHROME_URL_PATTERN_ + '$'); + + +/** + * RegExp pattern for function call in the Firefox stack trace. + * Creates 2 submatches with function name (optional) and arguments. + * + * Modern FF produces stack traces like: + * foo@url:1:2 + * a.b.foo@url:3:4 + * + * @private {string} + * @const + */ +goog.testing.stacktrace.FIREFOX_FUNCTION_CALL_PATTERN_ = '(' + + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + '(?:\\.' + + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + ')*' + + ')?' + + '(\\(.*\\))?@'; + + +/** + * Regular expression for parsing one stack frame in Firefox. + * @private {!RegExp} + * @const + */ +goog.testing.stacktrace.FIREFOX_STACK_FRAME_REGEXP_ = new RegExp( + '^' + goog.testing.stacktrace.FIREFOX_FUNCTION_CALL_PATTERN_ + '(?::0|' + + goog.testing.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 + */ +goog.testing.stacktrace.OPERA_ANONYMOUS_FUNCTION_NAME_PATTERN_ = + ''; + + +/** + * RegExp pattern for a function call in an Opera stack frame. + * Creates 4 (optional) submatches: the function name (if not anonymous), + * the aliased context object and function name (if anonymous), and the + * function call arguments. + * @private {string} + * @const + */ +goog.testing.stacktrace.OPERA_FUNCTION_CALL_PATTERN_ = '(?:(?:(' + + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + ')|' + + goog.testing.stacktrace.OPERA_ANONYMOUS_FUNCTION_NAME_PATTERN_ + + ')(\\(.*\\)))?@'; + + +/** + * Regular expression for parsing on stack frame in Opera 11.68 - 12.17. + * Newer versions of Opera use V8 and stack frames should match against + * goog.testing.stacktrace.V8_STACK_FRAME_REGEXP_. + * @private {!RegExp} + * @const + */ +goog.testing.stacktrace.OPERA_STACK_FRAME_REGEXP_ = new RegExp( + '^' + goog.testing.stacktrace.OPERA_FUNCTION_CALL_PATTERN_ + + goog.testing.stacktrace.URL_PATTERN_ + '?$'); + + +/** + * Regular expression for finding the function name in its source. + * @private {!RegExp} + * @const + */ +goog.testing.stacktrace.FUNCTION_SOURCE_REGEXP_ = new RegExp( + '^function (' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + ')'); + + +/** + * RegExp pattern for function call in a IE stack trace. This expression allows + * for identifiers like 'Anonymous function', 'eval code', and 'Global code'. + * @private {string} + * @const + */ +goog.testing.stacktrace.IE_FUNCTION_CALL_PATTERN_ = '(' + + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + '(?:\\.' + + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + ')*' + + '(?:\\s+\\w+)*)'; + + +/** + * Regular expression for parsing a stack frame in IE. + * @private {!RegExp} + * @const + */ +goog.testing.stacktrace.IE_STACK_FRAME_REGEXP_ = new RegExp( + '^ at ' + goog.testing.stacktrace.IE_FUNCTION_CALL_PATTERN_ + '\\s*\\(' + + '(' + + 'eval code:[^)]*' + + '|' + + 'Unknown script code:[^)]*' + + '|' + goog.testing.stacktrace.URL_PATTERN_ + ')\\)?$'); + + +/** + * Creates a stack trace by following the call chain. Based on + * {@link goog.debug.getStacktrace}. + * @return {!Array} Stack frames. + * @private + * @suppress {es5Strict} + */ +goog.testing.stacktrace.followCallChain_ = function() { + var frames = []; + var fn = arguments.callee.caller; + var depth = 0; + + while (fn && depth < goog.testing.stacktrace.MAX_DEPTH_) { + var fnString = Function.prototype.toString.call(fn); + var match = fnString.match(goog.testing.stacktrace.FUNCTION_SOURCE_REGEXP_); + var functionName = match ? match[1] : ''; + + frames.push(new goog.testing.stacktrace.Frame('', functionName, '', '')); + + + try { + fn = fn.caller; + } catch (e) { + break; + } + depth++; + } + + return frames; +}; + + +/** + * Parses one stack frame. + * @param {string} frameStr The stack frame as string. + * @return {goog.testing.stacktrace.Frame} Stack frame object or null if the + * parsing failed. + * @private + */ +goog.testing.stacktrace.parseStackFrame_ = function(frameStr) { + // This match includes newer versions of Opera (15+). + var m = frameStr.match(goog.testing.stacktrace.V8_STACK_FRAME_REGEXP_); + if (m) { + return new goog.testing.stacktrace.Frame( + m[1] || '', m[2] || '', m[3] || '', m[4] || m[5] || m[6] || ''); + } + + // TODO(johnlenz): remove this. It seems like if this was useful it would + // need to be before the V8 check. + if (frameStr.length > + goog.testing.stacktrace.MAX_FIREFOX_FRAMESTRING_LENGTH_) { + return null; + } + + m = frameStr.match(goog.testing.stacktrace.FIREFOX_STACK_FRAME_REGEXP_); + if (m) { + return new goog.testing.stacktrace.Frame('', m[1] || '', '', m[3] || ''); + } + + // Match against Presto Opera 11.68 - 12.17. + m = frameStr.match(goog.testing.stacktrace.OPERA_STACK_FRAME_REGEXP_); + if (m) { + return new goog.testing.stacktrace.Frame( + m[2] || '', m[1] || m[3] || '', '', m[5] || ''); + } + + m = frameStr.match(goog.testing.stacktrace.IE_STACK_FRAME_REGEXP_); + if (m) { + return new goog.testing.stacktrace.Frame('', m[1] || '', '', m[2] || ''); + } + + return null; +}; + + +/** + * Function to deobfuscate function names. + * @type {function(string): string} + * @private + */ +goog.testing.stacktrace.deobfuscateFunctionName_; + + +/** + * Sets function to deobfuscate function names. + * @param {function(string): string} fn function to deobfuscate function names. + */ +goog.testing.stacktrace.setDeobfuscateFunctionName = function(fn) { + goog.testing.stacktrace.deobfuscateFunctionName_ = fn; +}; + + +/** + * Deobfuscates a compiled function name with the function passed to + * {@link #setDeobfuscateFunctionName}. Returns the original function name if + * the deobfuscator hasn't been set. + * @param {string} name The function name to deobfuscate. + * @return {string} The deobfuscated function name. + * @private + */ +goog.testing.stacktrace.maybeDeobfuscateFunctionName_ = function(name) { + return goog.testing.stacktrace.deobfuscateFunctionName_ ? + goog.testing.stacktrace.deobfuscateFunctionName_(name) : + name; +}; + + +/** + * Escapes the special character in HTML. + * @param {string} text Plain text. + * @return {string} Escaped text. + * @private + */ +goog.testing.stacktrace.htmlEscape_ = function(text) { + return text.replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +}; + + +/** + * Converts the stack frames into canonical format. Chops the beginning and the + * end of it which come from the testing environment, not from the test itself. + * @param {!Array} frames The frames. + * @return {string} Canonical, pretty printed stack trace. + * @private + */ +goog.testing.stacktrace.framesToString_ = function(frames) { + // Removes the anonymous calls from the end of the stack trace (they come + // from testrunner.js, testcase.js and asserts.js), so the stack trace will + // end with the test... method. + var lastIndex = frames.length - 1; + while (frames[lastIndex] && frames[lastIndex].isAnonymous()) { + lastIndex--; + } + + // Removes the beginning of the stack trace until the call of the private + // _assert function (inclusive), so the stack trace will begin with a public + // asserter. Does nothing if _assert is not present in the stack trace. + var privateAssertIndex = -1; + for (var i = 0; i < frames.length; i++) { + if (frames[i] && frames[i].getName() == '_assert') { + privateAssertIndex = i; + break; + } + } + + var canonical = []; + for (var i = privateAssertIndex + 1; i <= lastIndex; i++) { + canonical.push('> '); + if (frames[i]) { + canonical.push(frames[i].toCanonicalString()); + } else { + canonical.push('(unknown)'); + } + canonical.push('\n'); + } + return canonical.join(''); +}; + + +/** + * Parses the browser's native stack trace. + * @param {string} stack Stack trace. + * @return {!Array} Stack frames. The + * unrecognized frames will be nulled out. + * @private + */ +goog.testing.stacktrace.parse_ = function(stack) { + var lines = stack.replace(/\s*$/, '').split('\n'); + var frames = []; + for (var i = 0; i < lines.length; i++) { + frames.push(goog.testing.stacktrace.parseStackFrame_(lines[i])); + } + return frames; +}; + + +/** + * Brings the stack trace into a common format across browsers. + * @param {string} stack Browser-specific stack trace. + * @return {string} Same stack trace in common format. + */ +goog.testing.stacktrace.canonicalize = function(stack) { + var frames = goog.testing.stacktrace.parse_(stack); + return goog.testing.stacktrace.framesToString_(frames); +}; + + +/** + * Returns the native stack trace. + * @return {string|!Array} + * @private + */ +goog.testing.stacktrace.getNativeStack_ = function() { + var tmpError = new Error(); + if (tmpError.stack) { + return tmpError.stack; + } + + // IE10 will only create a stack trace when the Error is thrown. + // We use null.x() to throw an exception because the closure compiler may + // replace "throw" with a function call in an attempt to minimize the binary + // size, which in turn has the side effect of adding an unwanted stack frame. + try { + null.x(); + } catch (e) { + return e.stack; + } + return ''; +}; + + +/** + * Gets the native stack trace if available otherwise follows the call chain. + * @return {string} The stack trace in canonical format. + */ +goog.testing.stacktrace.get = function() { + var stack = goog.testing.stacktrace.getNativeStack_(); + var frames; + if (!stack) { + frames = goog.testing.stacktrace.followCallChain_(); + } else if (goog.isArray(stack)) { + frames = goog.testing.stacktrace.callSitesToFrames_(stack); + } else { + frames = goog.testing.stacktrace.parse_(stack); + } + return goog.testing.stacktrace.framesToString_(frames); +}; + + +/** + * Converts an array of CallSite (elements of a stack trace in V8) to an array + * of Frames. + * @param {!Array} stack The stack as an array of CallSites. + * @return {!Array} The stack as an array of + * Frames. + * @private + */ +goog.testing.stacktrace.callSitesToFrames_ = function(stack) { + var frames = []; + for (var i = 0; i < stack.length; i++) { + var callSite = stack[i]; + var functionName = callSite.getFunctionName() || 'unknown'; + var fileName = callSite.getFileName(); + var path = fileName ? + fileName + ':' + callSite.getLineNumber() + ':' + + callSite.getColumnNumber() : + 'unknown'; + frames.push(new goog.testing.stacktrace.Frame('', functionName, '', path)); + } + return frames; +}; + + +goog.exportSymbol( + 'setDeobfuscateFunctionName', + goog.testing.stacktrace.setDeobfuscateFunctionName); diff --git a/closure-library/closure/goog/testing/storage/fakemechanism.js b/closure-library/closure/goog/testing/storage/fakemechanism.js new file mode 100644 index 0000000000..23c18e593e --- /dev/null +++ b/closure-library/closure/goog/testing/storage/fakemechanism.js @@ -0,0 +1,69 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Provides a fake storage mechanism for testing. + * @author chrishenry@google.com (Chris Henry) + */ + +goog.provide('goog.testing.storage.FakeMechanism'); +goog.setTestOnly('goog.testing.storage.FakeMechanism'); + +goog.require('goog.storage.mechanism.IterableMechanism'); +goog.require('goog.structs.Map'); + + + +/** + * Creates a fake iterable mechanism. + * + * @constructor + * @extends {goog.storage.mechanism.IterableMechanism} + * @final + */ +goog.testing.storage.FakeMechanism = function() { + /** + * @type {goog.structs.Map} + * @private + */ + this.storage_ = new goog.structs.Map(); +}; +goog.inherits( + goog.testing.storage.FakeMechanism, + goog.storage.mechanism.IterableMechanism); + + +/** @override */ +goog.testing.storage.FakeMechanism.prototype.set = function(key, value) { + this.storage_.set(key, value); +}; + + +/** @override */ +goog.testing.storage.FakeMechanism.prototype.get = function(key) { + return /** @type {?string} */ ( + this.storage_.get(key, null /* default value */)); +}; + + +/** @override */ +goog.testing.storage.FakeMechanism.prototype.remove = function(key) { + this.storage_.remove(key); +}; + + +/** @override */ +goog.testing.storage.FakeMechanism.prototype.__iterator__ = function(opt_keys) { + return this.storage_.__iterator__(opt_keys); +}; diff --git a/closure-library/closure/goog/testing/strictmock.js b/closure-library/closure/goog/testing/strictmock.js new file mode 100644 index 0000000000..b147556443 --- /dev/null +++ b/closure-library/closure/goog/testing/strictmock.js @@ -0,0 +1,172 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview This file defines a strict mock implementation. + */ + +goog.setTestOnly('goog.testing.StrictMock'); +goog.provide('goog.testing.StrictMock'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.structs.Set'); +goog.require('goog.testing.Mock'); + + + +/** + * This is a mock that verifies that methods are called in the order that they + * are specified during the recording phase. Since it verifies order, it + * follows 'fail fast' semantics. If it detects a deviation from the + * expectations, it will throw an exception and not wait for verify to be + * called. + * @param {Object|Function} objectToMock The object that should be mocked, or + * the constructor of an object to mock. + * @param {boolean=} opt_mockStaticMethods An optional argument denoting that + * a mock should be constructed from the static functions of a class. + * @param {boolean=} opt_createProxy An optional argument denoting that + * a proxy for the target mock should be created. + * @constructor + * @extends {goog.testing.Mock} + * @final + */ +goog.testing.StrictMock = function( + objectToMock, opt_mockStaticMethods, opt_createProxy) { + goog.testing.Mock.call( + this, objectToMock, opt_mockStaticMethods, opt_createProxy); + + /** + * An array of MockExpectations. + * @type {!Array} + * @private + */ + this.$expectations_ = []; + + /** @private {!goog.structs.Set} */ + this.awaitingExpectations_ = new goog.structs.Set(); +}; +goog.inherits(goog.testing.StrictMock, goog.testing.Mock); + + +/** @override */ +goog.testing.StrictMock.prototype.$recordExpectation = function() { + if (this.$pendingExpectation) { + this.$expectations_.push(this.$pendingExpectation); + this.awaitingExpectations_.add(this.$pendingExpectation); + } +}; + + +/** @override */ +goog.testing.StrictMock.prototype.$recordCall = function(name, args) { + if (this.$expectations_.length == 0) { + this.$throwCallException(name, args); + } + + // If the current expectation has a different name, make sure it was called + // enough and then discard it. We're through with it. + var currentExpectation = this.$expectations_[0]; + while (!this.$verifyCall(currentExpectation, name, args)) { + // This might be an item which has passed its min, and we can now + // look past it, or it might be below its min and generate an error. + if (currentExpectation.actualCalls < currentExpectation.minCalls) { + this.$throwCallException(name, args, currentExpectation); + } + + this.$expectations_.shift(); + this.awaitingExpectations_.remove(currentExpectation); + this.maybeFinishedWithExpectations_(); + if (this.$expectations_.length < 1) { + // Nothing left, but this may be a failed attempt to call the previous + // item on the list, which may have been between its min and max. + this.$throwCallException(name, args, currentExpectation); + } + currentExpectation = this.$expectations_[0]; + } + + if (currentExpectation.maxCalls == 0) { + this.$throwCallException(name, args); + } + + currentExpectation.actualCalls++; + // If we hit the max number of calls for this expectation, we're finished + // with it. + if (currentExpectation.actualCalls == currentExpectation.maxCalls) { + this.$expectations_.shift(); + } + if (currentExpectation.actualCalls >= currentExpectation.minCalls) { + this.awaitingExpectations_.remove(currentExpectation); + this.maybeFinishedWithExpectations_(); + } + + return this.$do(currentExpectation, args); +}; + + +/** @override */ +goog.testing.StrictMock.prototype.$reset = function() { + goog.testing.StrictMock.superClass_.$reset.call(this); + + goog.array.clear(this.$expectations_); + this.awaitingExpectations_.clear(); +}; + + +/** @override */ +goog.testing.StrictMock.prototype.$waitAndVerify = function() { + for (var i = 0; i < this.$expectations_.length; i++) { + var expectation = this.$expectations_[i]; + goog.asserts.assert( + !isFinite(expectation.maxCalls) || + expectation.minCalls == expectation.maxCalls, + 'Mock expectations cannot have a loose number of expected calls to ' + + 'use $waitAndVerify.'); + } + var promise = goog.testing.StrictMock.base(this, '$waitAndVerify'); + this.maybeFinishedWithExpectations_(); + return promise; +}; + +/** + * @private + */ +goog.testing.StrictMock.prototype.maybeFinishedWithExpectations_ = function() { + var unresolvedExpectations = + goog.array.count(this.$expectations_, function(expectation) { + return expectation.actualCalls < expectation.minCalls; + }); + if (this.waitingForExpectations && !unresolvedExpectations) { + this.waitingForExpectations.resolve(); + } +}; + + +/** @override */ +goog.testing.StrictMock.prototype.$verify = function() { + goog.testing.StrictMock.superClass_.$verify.call(this); + + while (this.$expectations_.length > 0) { + var expectation = this.$expectations_[0]; + if (expectation.actualCalls < expectation.minCalls) { + this.$throwException( + 'Missing a call to ' + expectation.name + '\nExpected: ' + + expectation.minCalls + ' but was: ' + expectation.actualCalls); + + } else { + // Don't need to check max, that's handled when the call is made + this.$expectations_.shift(); + } + } +}; diff --git a/closure-library/closure/goog/testing/style/layoutasserts.js b/closure-library/closure/goog/testing/style/layoutasserts.js new file mode 100644 index 0000000000..d054b259e1 --- /dev/null +++ b/closure-library/closure/goog/testing/style/layoutasserts.js @@ -0,0 +1,317 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A utility class for making layout assertions. This is a port + * of http://go/layoutbot.java + * See {@link http://go/layouttesting}. + */ + +goog.setTestOnly('goog.testing.style.layoutasserts'); +goog.provide('goog.testing.style.layoutasserts'); + +goog.require('goog.style'); +goog.require('goog.testing.asserts'); +goog.require('goog.testing.style'); + + +/** + * Asserts that an element has: + * 1 - a CSS rendering the makes the element visible. + * 2 - a non-zero width and height. + * @param {Element|string} a The element or optionally the comment string. + * @param {Element=} opt_b The element when a comment string is present. + */ +var assertIsVisible = function(a, opt_b) { + _validateArguments(1, arguments); + var element = nonCommentArg(1, 1, arguments); + + _assert( + commentArg(1, arguments), goog.testing.style.isVisible(element) && + goog.testing.style.hasVisibleDimensions(element), + 'Specified element should be visible.'); +}; + + +/** + * The counter assertion of assertIsVisible(). + * @param {Element|string} a The element or optionally the comment string. + * @param {Element=} opt_b The element when a comment string is present. + */ +var assertNotVisible = function(a, opt_b) { + _validateArguments(1, arguments); + var element = nonCommentArg(1, 1, arguments); + if (!element) { + return; + } + + _assert( + commentArg(1, arguments), !goog.testing.style.isVisible(element) || + !goog.testing.style.hasVisibleDimensions(element), + 'Specified element should not be visible.'); +}; + + +/** + * Asserts that the two specified elements intersect. + * @param {Element|string} a The first element or optionally the comment string. + * @param {Element} b The second element or the first element if comment string + * is present. + * @param {Element=} opt_c The second element if comment string is present. + */ +var assertIntersect = function(a, b, opt_c) { + _validateArguments(2, arguments); + var element = nonCommentArg(1, 2, arguments); + var otherElement = nonCommentArg(2, 2, arguments); + + _assert( + commentArg(1, arguments), + goog.testing.style.intersects(element, otherElement), + 'Elements should intersect.'); +}; + + +/** + * Asserts that the two specified elements do not intersect. + * @param {Element|string} a The first element or optionally the comment string. + * @param {Element} b The second element or the first element if comment string + * is present. + * @param {Element=} opt_c The second element if comment string is present. + */ +var assertNoIntersect = function(a, b, opt_c) { + _validateArguments(2, arguments); + var element = nonCommentArg(1, 2, arguments); + var otherElement = nonCommentArg(2, 2, arguments); + + _assert( + commentArg(1, arguments), + !goog.testing.style.intersects(element, otherElement), + 'Elements should not intersect.'); +}; + + +/** + * Asserts that the element must have the specified width. + * @param {Element|string} a The first element or optionally the comment string. + * @param {Element} b The second element or the first element if comment string + * is present. + * @param {Element=} opt_c The second element if comment string is present. + */ +var assertWidth = function(a, b, opt_c) { + _validateArguments(2, arguments); + var element = nonCommentArg(1, 2, arguments); + var width = nonCommentArg(2, 2, arguments); + var size = goog.style.getSize(element); + var elementWidth = size.width; + + _assert( + commentArg(1, arguments), + goog.testing.style.layoutasserts.isWithinThreshold_( + width, elementWidth, 0 /* tolerance */), + 'Element should have width ' + width + ' but was ' + elementWidth + '.'); +}; + + +/** + * Asserts that the element must have the specified width within the specified + * tolerance. + * @param {Element|string} a The element or optionally the comment string. + * @param {number|Element} b The height or the element if comment string is + * present. + * @param {number} c The tolerance or the height if comment string is + * present. + * @param {number=} opt_d The tolerance if comment string is present. + */ +var assertWidthWithinTolerance = function(a, b, c, opt_d) { + _validateArguments(3, arguments); + var element = nonCommentArg(1, 3, arguments); + var width = nonCommentArg(2, 3, arguments); + var tolerance = nonCommentArg(3, 3, arguments); + var size = goog.style.getSize(element); + var elementWidth = size.width; + + _assert( + commentArg(1, arguments), + goog.testing.style.layoutasserts.isWithinThreshold_( + width, elementWidth, tolerance), + 'Element width(' + elementWidth + ') should be within given width(' + + width + ') with tolerance value of ' + tolerance + '.'); +}; + + +/** + * Asserts that the element must have the specified height. + * @param {Element|string} a The first element or optionally the comment string. + * @param {Element} b The second element or the first element if comment string + * is present. + * @param {Element=} opt_c The second element if comment string is present. + */ +var assertHeight = function(a, b, opt_c) { + _validateArguments(2, arguments); + var element = nonCommentArg(1, 2, arguments); + var height = nonCommentArg(2, 2, arguments); + var size = goog.style.getSize(element); + var elementHeight = size.height; + + _assert( + commentArg(1, arguments), + goog.testing.style.layoutasserts.isWithinThreshold_( + height, elementHeight, 0 /* tolerance */), + 'Element should have height ' + height + '.'); +}; + + +/** + * Asserts that the element must have the specified height within the specified + * tolerance. + * @param {Element|string} a The element or optionally the comment string. + * @param {number|Element} b The height or the element if comment string is + * present. + * @param {number} c The tolerance or the height if comment string is + * present. + * @param {number=} opt_d The tolerance if comment string is present. + */ +var assertHeightWithinTolerance = function(a, b, c, opt_d) { + _validateArguments(3, arguments); + var element = nonCommentArg(1, 3, arguments); + var height = nonCommentArg(2, 3, arguments); + var tolerance = nonCommentArg(3, 3, arguments); + var size = goog.style.getSize(element); + var elementHeight = size.height; + + _assert( + commentArg(1, arguments), + goog.testing.style.layoutasserts.isWithinThreshold_( + height, elementHeight, tolerance), + 'Element width(' + elementHeight + ') should be within given width(' + + height + ') with tolerance value of ' + tolerance + '.'); +}; + + +/** + * Asserts that the first element is to the left of the second element. + * @param {Element|string} a The first element or optionally the comment string. + * @param {Element} b The second element or the first element if comment string + * is present. + * @param {Element=} opt_c The second element if comment string is present. + */ +var assertIsLeftOf = function(a, b, opt_c) { + _validateArguments(2, arguments); + var element = nonCommentArg(1, 2, arguments); + var otherElement = nonCommentArg(2, 2, arguments); + var elementRect = goog.style.getBounds(element); + var otherElementRect = goog.style.getBounds(otherElement); + + _assert( + commentArg(1, arguments), elementRect.left < otherElementRect.left, + 'Elements should be left to right.'); +}; + + +/** + * Asserts that the first element is strictly left of the second element. + * @param {Element|string} a The first element or optionally the comment string. + * @param {Element} b The second element or the first element if comment string + * is present. + * @param {Element=} opt_c The second element if comment string is present. + */ +var assertIsStrictlyLeftOf = function(a, b, opt_c) { + _validateArguments(2, arguments); + var element = nonCommentArg(1, 2, arguments); + var otherElement = nonCommentArg(2, 2, arguments); + var elementRect = goog.style.getBounds(element); + var otherElementRect = goog.style.getBounds(otherElement); + + _assert( + commentArg(1, arguments), + elementRect.left + elementRect.width < otherElementRect.left, + 'Elements should be strictly left to right.'); +}; + + +/** + * Asserts that the first element is higher than the second element. + * @param {Element|string} a The first element or optionally the comment string. + * @param {Element} b The second element or the first element if comment string + * is present. + * @param {Element=} opt_c The second element if comment string is present. + */ +var assertIsAbove = function(a, b, opt_c) { + _validateArguments(2, arguments); + var element = nonCommentArg(1, 2, arguments); + var otherElement = nonCommentArg(2, 2, arguments); + var elementRect = goog.style.getBounds(element); + var otherElementRect = goog.style.getBounds(otherElement); + + _assert( + commentArg(1, arguments), elementRect.top < otherElementRect.top, + 'Elements should be top to bottom.'); +}; + + +/** + * Asserts that the first element is strictly higher than the second element. + * @param {Element|string} a The first element or optionally the comment string. + * @param {Element} b The second element or the first element if comment string + * is present. + * @param {Element=} opt_c The second element if comment string is present. + */ +var assertIsStrictlyAbove = function(a, b, opt_c) { + _validateArguments(2, arguments); + var element = nonCommentArg(1, 2, arguments); + var otherElement = nonCommentArg(2, 2, arguments); + var elementRect = goog.style.getBounds(element); + var otherElementRect = goog.style.getBounds(otherElement); + + _assert( + commentArg(1, arguments), + elementRect.top + elementRect.height < otherElementRect.top, + 'Elements should be strictly top to bottom.'); +}; + + +/** + * Asserts that the first element's bounds contain the bounds of the second + * element. + * @param {Element|string} a The first element or optionally the comment string. + * @param {Element} b The second element or the first element if comment string + * is present. + * @param {Element=} opt_c The second element if comment string is present. + */ +var assertContained = function(a, b, opt_c) { + _validateArguments(2, arguments); + var element = nonCommentArg(1, 2, arguments); + var otherElement = nonCommentArg(2, 2, arguments); + var elementRect = goog.style.getBounds(element); + var otherElementRect = goog.style.getBounds(otherElement); + + _assert( + commentArg(1, arguments), elementRect.contains(otherElementRect), + 'Element should be contained within the other element.'); +}; + + +/** + * Returns true if the difference between val1 and val2 is less than or equal to + * the threashold. + * @param {number} val1 The first value. + * @param {number} val2 The second value. + * @param {number} threshold The threshold value. + * @return {boolean} Whether or not the the values are within the threshold. + * @private + */ +goog.testing.style.layoutasserts.isWithinThreshold_ = function( + val1, val2, threshold) { + return Math.abs(val1 - val2) <= threshold; +}; diff --git a/closure-library/closure/goog/testing/style/style.js b/closure-library/closure/goog/testing/style/style.js new file mode 100644 index 0000000000..0adeeca562 --- /dev/null +++ b/closure-library/closure/goog/testing/style/style.js @@ -0,0 +1,81 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities for inspecting page layout. This is a port of + * http://go/layoutbot.java + * See {@link http://go/layouttesting}. + */ + +goog.setTestOnly('goog.testing.style'); +goog.provide('goog.testing.style'); + +goog.require('goog.dom'); +goog.require('goog.math.Rect'); +goog.require('goog.style'); + + +/** + * Determines whether the bounding rectangles of the given elements intersect. + * @param {Element} element The first element. + * @param {Element} otherElement The second element. + * @return {boolean} Whether the bounding rectangles of the given elements + * intersect. + */ +goog.testing.style.intersects = function(element, otherElement) { + var elementRect = goog.style.getBounds(element); + var otherElementRect = goog.style.getBounds(otherElement); + return goog.math.Rect.intersects(elementRect, otherElementRect); +}; + + +/** + * Determines whether the element has visible dimensions, i.e. x > 0 && y > 0. + * @param {Element} element The element to check. + * @return {boolean} Whether the element has visible dimensions. + */ +goog.testing.style.hasVisibleDimensions = function(element) { + var elSize = goog.style.getSize(element); + var shortest = elSize.getShortest(); + if (shortest <= 0) { + return false; + } + + return true; +}; + + +/** + * Determines whether the CSS style of the element renders it visible. + * @param {!Element} element The element to check. + * @return {boolean} Whether the CSS style of the element renders it visible. + */ +goog.testing.style.isVisible = function(element) { + var style = getComputedStyle(element); + return style.visibility != 'hidden' && style.display != 'none'; +}; + + +/** + * Test whether the given element is on screen. + * @param {!Element} el The element to test. + * @return {boolean} Whether the element is on the screen. + */ +goog.testing.style.isOnScreen = function(el) { + var doc = goog.dom.getDomHelper(el).getDocument(); + var viewport = goog.style.getVisibleRectForElement(doc.body); + var viewportRect = goog.math.Rect.createFromBox(viewport); + return goog.dom.contains(doc, el) && + goog.style.getBounds(el).intersects(viewportRect); +}; diff --git a/closure-library/closure/goog/testing/testcase.js b/closure-library/closure/goog/testing/testcase.js new file mode 100644 index 0000000000..03c88438e3 --- /dev/null +++ b/closure-library/closure/goog/testing/testcase.js @@ -0,0 +1,2237 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A class representing a set of test functions to be run. + * + * Testing code should not have dependencies outside of goog.testing so as to + * reduce the chance of masking missing dependencies. + * + * This file does not compile correctly with --collapse_properties. Use + * --property_renaming=ALL_UNQUOTED instead. + * + */ + +goog.setTestOnly('goog.testing.TestCase'); +goog.provide('goog.testing.TestCase'); +goog.provide('goog.testing.TestCase.Error'); +goog.provide('goog.testing.TestCase.Order'); +goog.provide('goog.testing.TestCase.Result'); +goog.provide('goog.testing.TestCase.Test'); + + +goog.require('goog.Promise'); +goog.require('goog.Thenable'); +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.object'); +goog.require('goog.testing.JsUnitException'); +goog.require('goog.testing.asserts'); + + + +/** + * A class representing a JsUnit test case. A TestCase is made up of a number + * of test functions which can be run. Individual test cases can override the + * following functions to set up their test environment: + * - runTests - completely override the test's runner + * - setUpPage - called before any of the test functions are run + * - tearDownPage - called after all tests are finished + * - setUp - called before each of the test functions + * - tearDown - called after each of the test functions + * - shouldRunTests - called before a test run, all tests are skipped if it + * returns false. Can be used to disable tests on browsers + * where they aren't expected to pass. + *

+ * TestCase objects are usually constructed by inspecting the global environment + * to discover functions that begin with the prefix test. + * (See {@link #autoDiscoverLifecycle} and {@link #autoDiscoverTests}.) + *

+ * + *

Testing asychronous code with promises

+ * + *

+ * In the simplest cases, the behavior that the developer wants to test + * is synchronous, and the test functions exercising the behavior execute + * synchronously. But TestCase can also be used to exercise asynchronous code + * through the use of + * promises. If a test function returns an object that has a + * then method defined on it, the test framework switches to an + * asynchronous execution strategy: the next test function will not begin + * execution until the returned promise is resolved or rejected. Instead of + * writing test assertions at the top level inside a test function, the test + * author chains them on the end of the returned promise. For example: + *

+ *
+ *   function testPromiseBasedAPI() {
+ *     return promiseBasedAPI().then(function(value) {
+ *       // Will run when the promise resolves, and before the next
+ *       // test function begins execution.
+ *       assertEquals('foo', value.bar);
+ *     });
+ *   }
+ * 
+ *

+ * Synchronous and asynchronous tests can be mixed in the same TestCase. + * Test functions that return an object with a then method are + * executed asynchronously, and all other test functions are executed + * synchronously. While this is convenient for test authors (since it doesn't + * require any explicit configuration for asynchronous tests), it can lead to + * confusion if the test author forgets to return the promise from the test + * function. For example: + *

+ *
+ *   function testPromiseBasedAPI() {
+ *     // This test should never succeed.
+ *     promiseBasedAPI().then(fail, fail);
+ *     // Oops! The promise isn't returned to the framework,
+ *     // so this test actually does succeed.
+ *   }
+ * 
+ *

+ * Since the test framework knows nothing about the promise created + * in the test function, it will run the function synchronously, record + * a success, and proceed immediately to the next test function. + *

+ *

+ * Promises returned from test functions can time out. If a returned promise + * is not resolved or rejected within {@link promiseTimeout} milliseconds, + * the test framework rejects the promise without a timeout error message. + * Test cases can configure the value of `promiseTimeout` by setting + *

+ *   goog.testing.TestCase.getActiveTestCase().promiseTimeout = ...
+ * 
+ * in their `setUpPage` methods. + *

+ * + * @param {string=} opt_name The name of the test case, defaults to + * 'Untitled Test Case'. + * @constructor + */ +goog.testing.TestCase = function(opt_name) { + /** + * A name for the test case. + * @type {string} + * @private + */ + this.name_ = opt_name || 'Untitled Test Case'; + + /** + * If the test should be auto discovered via {@link #autoDiscoverTests} when + * test case is initialized. + * @type {boolean} + * @private + */ + this.shouldAutoDiscoverTests_ = true; + + /** + * Array of test functions that can be executed. + * @type {!Array} + * @private + */ + this.tests_ = []; + + /** + * Set of test names and/or indices to execute, or null if all tests should + * be executed. + * + * Indices are included to allow automation tools to run a subset of the + * tests without knowing the exact contents of the test file. + * + * Indices should only be used with SORTED ordering. + * + * Example valid values: + *
    + *
  • [testName] + *
  • [testName1, testName2] + *
  • [2] - will run the 3rd test in the order specified + *
  • [1,3,5] + *
  • [testName1, testName2, 3, 5] - will work + *
      + * @type {Object} + * @private + */ + this.testsToRun_ = null; + + /** + * A call back for each test. + * @private {?function(goog.testing.TestCase.Test, !Array)} + */ + this.testDone_ = null; + + /** + * The order to run the auto-discovered tests in. + * @type {string} + */ + this.order = goog.testing.TestCase.Order.SORTED; + + /** @private {function(!goog.testing.TestCase.Result)} */ + this.runNextTestCallback_ = goog.nullFunction; + + /** + * The currently executing test case or null. + * @private {?goog.testing.TestCase.Test} + */ + this.curTest_ = null; + + /** + * Object used to encapsulate the test results. + * @type {!goog.testing.TestCase.Result} + * @protected + * @suppress {underscore|visibility} + */ + this.result_ = new goog.testing.TestCase.Result(this); + + /** + * An array of exceptions generated by `assert` statements. + * @private {!Array} + */ + this.thrownAssertionExceptions_ = []; + + /** + * Whether the test should fail if exceptions arising from an assert statement + * never bubbled up to the testing framework. + * @type {boolean} + */ + this.failOnUnreportedAsserts = true; + + /** + * The maximum time in milliseconds a promise returned from a test function + * may remain pending before the test fails due to timeout. + * @type {number} + */ + this.promiseTimeout = 1000; // 1s + + /** + * Callbacks that will be executed when the test has finalized. + * @private {!Array} + */ + this.onCompletedCallbacks_ = []; + + /** @type {number|undefined} */ + this.endTime_; + + /** @private {number} */ + this.testsRanSoFar_ = 0; +}; + + +/** + * The order to run the auto-discovered tests. + * @enum {string} + */ +goog.testing.TestCase.Order = { + /** + * This is browser dependent and known to be different in FF and Safari + * compared to others. + */ + NATURAL: 'natural', + + /** Random order. */ + RANDOM: 'random', + + /** Sorted based on the name. */ + SORTED: 'sorted' +}; + + +/** + * @return {string} The name of the test. + */ +goog.testing.TestCase.prototype.getName = function() { + return this.name_; +}; + +/** + * Returns the current test or null. + * @return {?goog.testing.TestCase.Test} + * @protected + */ +goog.testing.TestCase.prototype.getCurrentTest = function() { + return this.curTest_; +}; + +/** + * The maximum amount of time in milliseconds that the test case can take + * before it is forced to yield and reschedule. This prevents the test runner + * from blocking the browser and potentially hurting the test harness. + * @type {number} + */ +goog.testing.TestCase.maxRunTime = 200; + + +/** + * Save a reference to `window.setTimeout`, so any code that overrides the + * default behavior (the MockClock, for example) doesn't affect our runner. + * @type {function((Function|string), number=, *=): number} + * @private + */ +goog.testing.TestCase.protectedSetTimeout_ = goog.global.setTimeout; + + +/** + * Save a reference to `window.clearTimeout`, so any code that overrides + * the default behavior (e.g. MockClock) doesn't affect our runner. + * @type {function((null|number|undefined)): void} + * @private + */ +goog.testing.TestCase.protectedClearTimeout_ = goog.global.clearTimeout; + + +/** + * Save a reference to `window.Date`, so any code that overrides + * the default behavior doesn't affect our runner. + * @type {function(new: Date)} + * @private + */ +goog.testing.TestCase.protectedDate_ = Date; + +/** + * Save a reference to `window.performance`, so any code that overrides + * the default behavior doesn't affect our runner. + * @type {?Performance} + * @private + */ +goog.testing.TestCase.protectedPerformance_ = typeof window !== 'undefined' && + window.performance && window.performance.now ? + performance : + null; + + +/** + * Name of the current test that is running, or null if none is running. + * @type {?string} + */ +goog.testing.TestCase.currentTestName = null; + + +/** + * Avoid a dependency on goog.userAgent and keep our own reference of whether + * the browser is IE. + * @type {boolean} + */ +goog.testing.TestCase.IS_IE = typeof opera == 'undefined' && + !!goog.global.navigator && + goog.global.navigator.userAgent.indexOf('MSIE') != -1; + + +/** + * Exception object that was detected before a test runs. + * @type {*} + * @protected + */ +goog.testing.TestCase.prototype.exceptionBeforeTest; + + +/** + * Whether the test case has ever tried to execute. + * @type {boolean} + */ +goog.testing.TestCase.prototype.started = false; + + +/** + * Whether the test case is running. + * @type {boolean} + */ +goog.testing.TestCase.prototype.running = false; + + +/** + * Timestamp for when the test was started. + * @type {number} + * @private + */ +goog.testing.TestCase.prototype.startTime_ = 0; + + +/** + * Time since the last batch of tests was started, if batchTime exceeds + * {@link #maxRunTime} a timeout will be used to stop the tests blocking the + * browser and a new batch will be started. + * @type {number} + * @private + */ +goog.testing.TestCase.prototype.batchTime_ = 0; + + +/** + * Pointer to the current test. + * @type {number} + * @private + */ +goog.testing.TestCase.prototype.currentTestPointer_ = 0; + + +/** + * Adds a new test to the test case. + * @param {!goog.testing.TestCase.Test} test The test to add. + */ +goog.testing.TestCase.prototype.add = function(test) { + goog.asserts.assert(test); + if (this.started) { + throw new Error( + 'Tests cannot be added after execute() has been called. ' + + 'Test: ' + test.name); + } + + this.tests_.push(test); +}; + + +/** + * Creates and adds a new test. + * + * Convenience function to make syntax less awkward when not using automatic + * test discovery. + * + * @param {string} name The test name. + * @param {function()} ref Reference to the test function. + * @param {!Object=} scope Optional scope that the test function should be + * called in. + * @param {!Array=} objChain An array of Objects that may have + * additional set up/tear down logic for a particular test. + */ +goog.testing.TestCase.prototype.addNewTest = function( + name, ref, scope, objChain) { + this.add(this.createTest(name, ref, scope || this, objChain)); +}; + + +/** + * Sets the tests. + * @param {!Array} tests A new test array. + * @protected + */ +goog.testing.TestCase.prototype.setTests = function(tests) { + this.tests_ = tests; +}; + + +/** + * Gets the tests. + * @return {!Array} The test array. + */ +goog.testing.TestCase.prototype.getTests = function() { + return this.tests_; +}; + + +/** + * Returns the number of tests contained in the test case. + * @return {number} The number of tests. + */ +goog.testing.TestCase.prototype.getCount = function() { + return this.tests_.length; +}; + + +/** + * Returns the number of tests actually run in the test case, i.e. subtracting + * any which are skipped. + * @return {number} The number of un-ignored tests. + */ +goog.testing.TestCase.prototype.getActuallyRunCount = function() { + return this.testsToRun_ ? goog.object.getCount(this.testsToRun_) : 0; +}; + + +/** + * Returns the current test and increments the pointer. + * @return {goog.testing.TestCase.Test} The current test case. + */ +goog.testing.TestCase.prototype.next = function() { + var test; + while ((test = this.tests_[this.currentTestPointer_++])) { + if (!this.testsToRun_ || this.testsToRun_[test.name] || + this.testsToRun_[this.currentTestPointer_ - 1]) { + return test; + } + } + return null; +}; + + +/** + * Resets the test case pointer, so that next returns the first test. + */ +goog.testing.TestCase.prototype.reset = function() { + this.currentTestPointer_ = 0; + this.result_ = new goog.testing.TestCase.Result(this); +}; + + +/** + * Adds a callback function that should be executed when the tests have + * completed. + * @param {function()} fn The callback function. + */ +goog.testing.TestCase.prototype.addCompletedCallback = function(fn) { + this.onCompletedCallbacks_.push(fn); +}; + + +/** + * @param {goog.testing.TestCase.Order} order The sort order for running tests. + */ +goog.testing.TestCase.prototype.setOrder = function(order) { + this.order = order; +}; + + +/** + * @param {Object} testsToRun Set of tests to run. Entries in + * the set may be test names, like "testFoo", or numeric indices. Only + * tests identified by name or by index will be executed. + */ +goog.testing.TestCase.prototype.setTestsToRun = function(testsToRun) { + this.testsToRun_ = testsToRun; +}; + + +/** + * Can be overridden in test classes to indicate whether the tests in a case + * should be run in that particular situation. For example, this could be used + * to stop tests running in a particular browser, where browser support for + * the class under test was absent. + * @return {boolean} Whether any of the tests in the case should be run. + */ +goog.testing.TestCase.prototype.shouldRunTests = function() { + return true; +}; + + +/** + * Executes the tests, yielding asynchronously if execution time exceeds + * {@link maxRunTime}. There is no guarantee that the test case has finished + * once this method has returned. To be notified when the test case + * has finished, use {@link #addCompletedCallback} or + * {@link #runTestsReturningPromise}. + */ +goog.testing.TestCase.prototype.execute = function() { + if (!this.prepareForRun_()) { + return; + } + this.groupLogsStart(); + this.log('Starting tests: ' + this.name_); + this.cycleTests(); +}; + + +/** + * Sets up the internal state of the test case for a run. + * @return {boolean} If false, preparation failed because the test case + * is not supposed to run in the present environment. + * @private + */ +goog.testing.TestCase.prototype.prepareForRun_ = function() { + this.started = true; + this.reset(); + this.startTime_ = this.now(); + this.running = true; + this.result_.totalCount = this.getCount(); + if (!this.shouldRunTests()) { + this.log('shouldRunTests() returned false, skipping these tests.'); + this.result_.testSuppressed = true; + this.finalize(); + return false; + } + return true; +}; + + +/** + * Finalizes the test case, called when the tests have finished executing. + */ +goog.testing.TestCase.prototype.finalize = function() { + this.saveMessage('Done'); + + this.tearDownPage(); + + this.endTime_ = this.now(); + this.running = false; + this.result_.runTime = this.endTime_ - this.startTime_; + this.result_.numFilesLoaded = this.countNumFilesLoaded_(); + this.result_.complete = true; + this.testsRanSoFar_++; + + this.log(this.result_.getSummary()); + if (this.result_.isSuccess()) { + this.log('Tests complete'); + } else { + this.log('Tests Failed'); + } + goog.array.forEach(this.onCompletedCallbacks_, function(cb) { + cb(); + }); + this.onCompletedCallbacks_ = []; + this.groupLogsEnd(); +}; + + +/** + * Saves a message to the result set. + * @param {string} message The message to save. + */ +goog.testing.TestCase.prototype.saveMessage = function(message) { + this.result_.messages.push(this.getTimeStamp_() + ' ' + message); +}; + + +/** + * @return {boolean} Whether the test case is running inside the multi test + * runner. + */ +goog.testing.TestCase.prototype.isInsideMultiTestRunner = function() { + var top = goog.global['top']; + return top && typeof top['_allTests'] != 'undefined'; +}; + +/** + * @return {boolean} Whether the test-progress should be logged to the console. + */ +goog.testing.TestCase.prototype.shouldLogTestProgress = function() { + return !goog.global['skipClosureTestProgress'] && + !this.isInsideMultiTestRunner(); +}; + +/** + * Logs an object to the console, if available. + * @param {*} val The value to log. Will be ToString'd. + */ +goog.testing.TestCase.prototype.log = function(val) { + if (this.shouldLogTestProgress() && goog.global.console) { + if (typeof val == 'string') { + val = this.getTimeStamp_() + ' : ' + val; + } + if (val instanceof Error && val.stack) { + goog.global.console.log(val.stack); + } else { + goog.global.console.log(val); + } + } +}; + + +/** + * Groups the upcoming logs in the same log group + */ +goog.testing.TestCase.prototype.groupLogsStart = function() { + if (!this.isInsideMultiTestRunner() && goog.global.console && + goog.global.console.group) { + goog.global.console.group( + 'Test #' + (this.testsRanSoFar_ + 1) + ': ' + this.name_); + } +}; + + +/** + * Closes the group of the upcoming logs + */ +goog.testing.TestCase.prototype.groupLogsEnd = function() { + if (!this.isInsideMultiTestRunner() && goog.global.console && + goog.global.console.groupEnd) { + goog.global.console.groupEnd(); + } +}; + + +/** + * @return {boolean} Whether the test was a success. + */ +goog.testing.TestCase.prototype.isSuccess = function() { + return !!this.result_ && this.result_.isSuccess(); +}; + + +/** + * Returns a string detailing the results from the test. + * @param {boolean=} opt_verbose If true results will include data about all + * tests, not just what failed. + * @return {string} The results from the test. + */ +goog.testing.TestCase.prototype.getReport = function(opt_verbose) { + var rv = []; + + if (this.running) { + rv.push(this.name_ + ' [RUNNING]'); + } else if (this.result_.runCount == 0) { + rv.push(this.name_ + ' [NO TESTS RUN]'); + } else { + var label = this.result_.isSuccess() ? 'PASSED' : 'FAILED'; + rv.push(this.name_ + ' [' + label + ']'); + } + + if (goog.global.location) { + rv.push(this.trimPath_(goog.global.location.href)); + } + + rv.push(this.result_.getSummary()); + + if (opt_verbose) { + rv.push('.', this.result_.messages.join('\n')); + } else if (!this.result_.isSuccess()) { + rv.push(this.result_.errors.join('\n')); + } + + rv.push(' '); + + return rv.join('\n'); +}; + + +/** + * Returns the test results. + * @return {!goog.testing.TestCase.Result} + * @package + */ +goog.testing.TestCase.prototype.getResult = function() { + return this.result_; +}; + + +/** + * Returns the amount of time it took for the test to run. + * @return {number} The run time, in milliseconds. + */ +goog.testing.TestCase.prototype.getRunTime = function() { + return this.result_.runTime; +}; + + +/** + * Returns the number of script files that were loaded in order to run the test. + * @return {number} The number of script files. + */ +goog.testing.TestCase.prototype.getNumFilesLoaded = function() { + return this.result_.numFilesLoaded; +}; + + +/** + * Represents a test result. + * @typedef {{ + * 'source': string, + * 'message': string, + * 'stacktrace': string + * }} + */ +goog.testing.TestCase.IResult; + +/** + * Returns the test results object: a map from test names to a list of test + * failures (if any exist). + * @return {!Object>} Test + * results object. + */ +goog.testing.TestCase.prototype.getTestResults = function() { + var map = {}; + goog.object.forEach(this.result_.resultsByName, function(resultArray, key) { + // Make sure we only use properties on the actual map + if (!Object.prototype.hasOwnProperty.call( + this.result_.resultsByName, key)) { + return; + } + map[key] = []; + for (var j = 0; j < resultArray.length; j++) { + map[key].push(resultArray[j].toObject_()); + } + }, this); + return map; +}; + +/** + * Executes each of the tests, yielding asynchronously if execution time + * exceeds {@link #maxRunTime}. There is no guarantee that the test case + * has finished execution once this method has returned. + * To be notified when the test case has finished execution, use + * {@link #addCompletedCallback} or {@link #runTestsReturningPromise}. + * + * Overridable by the individual test case. This allows test cases to defer + * when the test is actually started. If overridden, finalize must be + * called by the test to indicate it has finished. + */ +goog.testing.TestCase.prototype.runTests = function() { + goog.testing.Continuation_.run(this.runSetUpPage_(this.execute)); +}; + + +/** + * Executes each of the tests, returning a promise that resolves with the + * test results once they are done running. + * @return {!IThenable} + * @final + * @package + */ +goog.testing.TestCase.prototype.runTestsReturningPromise = function() { + return new goog.Promise(function(resolve) { + goog.testing.Continuation_.run(this.runSetUpPage_(function() { + if (!this.prepareForRun_()) { + resolve(this.result_); + return; + } + this.groupLogsStart(); + this.log('Starting tests: ' + this.name_); + this.saveMessage('Start'); + this.batchTime_ = this.now(); + this.runNextTestCallback_ = resolve; + goog.testing.Continuation_.run(this.runNextTest_()); + })); + }, this); +}; + + +/** + * Runs the setUpPage methods. + * @param {function(this:goog.testing.TestCase)} runTestsFn Callback to invoke + * after setUpPage has completed. + * @return {?goog.testing.Continuation_} + * @private + */ +goog.testing.TestCase.prototype.runSetUpPage_ = function(runTestsFn) { + return this.invokeFunction_(this.setUpPage, runTestsFn, function(e) { + this.exceptionBeforeTest = e; + runTestsFn.call(this); + }, 'setUpPage'); +}; + + +/** + * Executes the next test method synchronously or with promises, depending on + * the test method's return value. + * + * If the test method returns a promise, the next test method will run once + * the promise is resolved or rejected. If the test method does not + * return a promise, it is assumed to be synchronous, and execution proceeds + * immediately to the next test method. This means that test cases can run + * partially synchronously and partially asynchronously, depending on + * the return values of their test methods. In particular, a test case + * executes synchronously until the first promise is returned from a + * test method (or until a resource limit is reached; see + * {@link finishTestInvocation_}). + * @return {?goog.testing.Continuation_} + * @private + */ +goog.testing.TestCase.prototype.runNextTest_ = function() { + this.curTest_ = this.next(); + if (!this.curTest_ || !this.running) { + this.finalize(); + return new goog.testing.Continuation_( + goog.bind(this.runNextTestCallback_, this, this.result_)); + } + + var shouldRunTest = true; + try { + shouldRunTest = this.shouldRunTestsHelper_(); + } catch (error) { + this.curTest_.name = 'shouldRunTests for ' + this.curTest_.name; + return new goog.testing.Continuation_( + goog.bind(this.finishTestInvocation_, this, error)); + } + + if (!shouldRunTest) { + return new goog.testing.Continuation_( + goog.bind(this.finishTestInvocation_, this)); + } + + this.curTest_.started(); + this.result_.runCount++; + this.log('Running test: ' + this.curTest_.name); + if (this.maybeFailTestEarly(this.curTest_)) { + return new goog.testing.Continuation_( + goog.bind(this.finishTestInvocation_, this)); + } + goog.testing.TestCase.currentTestName = this.curTest_.name; + return this.safeSetUp_(); +}; + + +/** + * @return {boolean} + * @private + */ +goog.testing.TestCase.prototype.shouldRunTestsHelper_ = function() { + var objChain = + this.curTest_.objChain.length ? this.curTest_.objChain : [this]; + + for (var i = 0; i < objChain.length; i++) { + var obj = objChain[i]; + + if (!goog.isFunction(obj.shouldRunTests)) { + return true; + } + + if (goog.isFunction(obj.shouldRunTests['$cachedResult'])) { + if (!obj.shouldRunTests['$cachedResult']()) { + return false; + } + } + + var result; + (function() { + // Cache the result by storing a function. This way we only call + // shouldRunTests once per object in the chain. This enforces that people + // do not attempt to suppress some tests and not others with the same + // shouldRunTests function. + try { + var cached = result = obj.shouldRunTests.call(obj); + obj.shouldRunTests['$cachedResult'] = function() { + return cached; + }; + } catch (error) { + obj.shouldRunTests['$cachedResult'] = function() { + throw error; + }; + throw error; + } + })(); + + if (!result) { + this.result_.suppressedTests.push(this.curTest_.name); + return false; + } + } + + return true; +}; + +/** + * Runs all the setups associated with a test. + * @return {?goog.testing.Continuation_} + * @private + */ +goog.testing.TestCase.prototype.safeSetUp_ = function() { + var setUps = + this.curTest_.setUps.length ? this.curTest_.setUps.slice() : [this.setUp]; + return this.safeSetUpHelper_(setUps).call(this); +}; + +/** + * Recursively invokes setUp functions. + * @param {!Array} setUps + * @return {function(): ?goog.testing.Continuation_} + * @private + */ +goog.testing.TestCase.prototype.safeSetUpHelper_ = function(setUps) { + if (!setUps.length) { + return this.safeRunTest_; + } + return goog.bind( + this.invokeFunction_, this, setUps.shift(), this.safeSetUpHelper_(setUps), + this.safeTearDown_, 'setUp'); +}; + +/** + * Calls the given test function, handling errors appropriately. + * @return {?goog.testing.Continuation_} + * @private + */ +goog.testing.TestCase.prototype.safeRunTest_ = function() { + return this.invokeFunction_( + goog.bind(this.curTest_.ref, this.curTest_.scope), this.safeTearDown_, + this.safeTearDown_, this.curTest_.name); +}; + + +/** + * Calls {@link tearDown}, handling errors appropriately. + * @param {*=} opt_error Error associated with the test, if any. + * @return {?goog.testing.Continuation_} + * @private + */ +goog.testing.TestCase.prototype.safeTearDown_ = function(opt_error) { + // If the test itself failed, report that before running any tearDown()s. + if (arguments.length == 1) { + this.recordError(this.curTest_.name, opt_error); + } + var tearDowns = this.curTest_.tearDowns.length ? + this.curTest_.tearDowns.slice() : + [this.tearDown]; + return this.safeTearDownHelper_(tearDowns).call(this); +}; + +/** + * Recursively invokes tearDown functions. + * @param {!Array} tearDowns + * @return {function(): ?goog.testing.Continuation_} + * @private + */ +goog.testing.TestCase.prototype.safeTearDownHelper_ = function(tearDowns) { + if (!tearDowns.length) { + return this.finishTestInvocation_; + } + return goog.bind( + this.invokeFunction_, this, tearDowns.shift(), + this.safeTearDownHelper_(tearDowns), this.finishTestInvocation_, + 'tearDown'); +}; + + +/** + * Calls the given `fn`, then calls either `onSuccess` or + * `onFailure`, either synchronously or using promises, depending on + * `fn`'s return value. + * + * If `fn` throws an exception, `onFailure` is called immediately + * with the exception. + * + * If `fn` returns a promise, and the promise is eventually resolved, + * `onSuccess` is called with no arguments. If the promise is eventually + * rejected, `onFailure` is called with the rejection reason. + * + * Otherwise, if `fn` neither returns a promise nor throws an exception, + * `onSuccess` is called immediately with no arguments. + * + * `fn`, `onSuccess`, and `onFailure` are all called with + * the TestCase instance as the method receiver. + * + * @param {function()} fn The function to call. + * @param {function(this:goog.testing.TestCase): (?goog.testing.Continuation_|undefined)} onSuccess + * @param {function(this:goog.testing.TestCase, *): (?goog.testing.Continuation_|undefined)} onFailure + * @param {string} fnName Name of the function being invoked e.g. 'setUp'. + * @return {?goog.testing.Continuation_} + * @private + */ +goog.testing.TestCase.prototype.invokeFunction_ = function( + fn, onSuccess, onFailure, fnName) { + var self = this; + this.thrownAssertionExceptions_ = []; + try { + var retval = fn.call(this); + if (goog.Thenable.isImplementedBy(retval) || + goog.isFunction(retval && retval['then'])) { + // Resolve Thenable into a proper Promise to avoid hard to debug + // problems. + var promise = goog.Promise.resolve(retval); + promise = this.rejectIfPromiseTimesOut_( + promise, self.promiseTimeout, + 'Timed out while waiting for a promise returned from ' + fnName + + ' to resolve. Set goog.testing.TestCase.getActiveTestCase()' + + '.promiseTimeout to adjust the timeout.'); + promise.then( + function() { + self.resetBatchTimeAfterPromise_(); + if (self.thrownAssertionExceptions_.length == 0) { + goog.testing.Continuation_.run(onSuccess.call(self)); + } else { + goog.testing.Continuation_.run(onFailure.call( + self, self.reportUnpropagatedAssertionExceptions_(fnName))); + } + }, + function(e) { + self.reportUnpropagatedAssertionExceptions_(fnName, e); + self.resetBatchTimeAfterPromise_(); + goog.testing.Continuation_.run(onFailure.call(self, e)); + }); + return null; + } else { + if (this.thrownAssertionExceptions_.length == 0) { + return new goog.testing.Continuation_(goog.bind(onSuccess, this)); + } else { + return new goog.testing.Continuation_(goog.bind( + onFailure, this, + this.reportUnpropagatedAssertionExceptions_(fnName))); + } + } + } catch (e) { + this.reportUnpropagatedAssertionExceptions_(fnName, e); + return new goog.testing.Continuation_(goog.bind(onFailure, this, e)); + } +}; + + +/** + * Logs all of the exceptions generated from failing assertions, and returns a + * generic exception informing the user that one or more exceptions were not + * propagated, causing the test to erroneously pass. + * + * This is also called when a test fails so that the user sees swallowed errors. + * (This can make it much easier to debug failures in callbacks in catch blocks) + * If the actually-thrown error (that made the test fail) is also a JSUnit error + * (which will therefore be in this array), it will be silently deduped when the + * regular failure handler tries to record it again. + * @param {string} testName The test function's name. + * @param {*=} actualError The thrown error the made the test fail, if any + * @return {!goog.testing.JsUnitException} + * @private + */ +goog.testing.TestCase.prototype.reportUnpropagatedAssertionExceptions_ = + function(testName, actualError) { + var extraExceptions = this.thrownAssertionExceptions_.slice(); + // If the actual error isn't a JSUnit exception, it won't be in this array. + goog.array.remove(extraExceptions, actualError); + var numExceptions = extraExceptions.length; + if (numExceptions && actualError) { + // Don't log this message if the only exception is the actual failure. + var message = + numExceptions + ' additional exceptions were swallowed by the test:'; + this.log(message); + this.saveMessage(message); + } + + + for (var i = 0; i < numExceptions; i++) { + this.recordError(testName, extraExceptions[i]); + } + + // Mark the test as failed. + return new goog.testing.JsUnitException( + 'One or more assertions were raised but not caught by the testing ' + + 'framework. These assertions may have been unintentionally captured ' + + 'by a catch block or a thenCatch resolution of a Promise.'); +}; + + +/** + * Resets the batch run timer. This should only be called after resolving a + * promise since Promise.then() has an implicit yield. + * @private + */ +goog.testing.TestCase.prototype.resetBatchTimeAfterPromise_ = function() { + this.batchTime_ = this.now(); +}; + + +/** + * Finishes up bookkeeping for the current test function, and schedules + * the next test function to run, either immediately or asychronously. + * @param {*=} opt_error Optional error resulting from the test invocation. + * @return {?goog.testing.Continuation_} + * @private + */ +goog.testing.TestCase.prototype.finishTestInvocation_ = function(opt_error) { + if (arguments.length == 1) { + this.recordError(this.curTest_.name, opt_error); + } + + // If no errors have been recorded for the test, it is a success. + if (!(this.curTest_.name in this.result_.resultsByName) || + !this.result_.resultsByName[this.curTest_.name].length) { + if (goog.array.indexOf(this.result_.suppressedTests, this.curTest_.name) >= + 0) { + this.doSkipped(this.curTest_); + } else { + this.doSuccess(this.curTest_); + } + } else { + this.doError(this.curTest_); + } + + goog.testing.TestCase.currentTestName = null; + + // If the test case has consumed too much time or stack space, + // yield to avoid blocking the browser. Otherwise, proceed to the next test. + if (this.now() - this.batchTime_ > goog.testing.TestCase.maxRunTime) { + this.saveMessage('Breaking async'); + this.timeout(goog.bind(this.startNextBatch_, this), 0); + return null; + } else { + return new goog.testing.Continuation_(goog.bind(this.runNextTest_, this)); + } +}; + + +/** + * Start a new batch to tests after yielding, resetting batchTime and depth. + * @private + */ +goog.testing.TestCase.prototype.startNextBatch_ = function() { + this.batchTime_ = this.now(); + goog.testing.Continuation_.run(this.runNextTest_()); +}; + + +/** + * Reorders the tests depending on the `order` field. + * @private + */ +goog.testing.TestCase.prototype.orderTests_ = function() { + switch (this.order) { + case goog.testing.TestCase.Order.RANDOM: + // Fisher-Yates shuffle + var i = this.tests_.length; + while (i > 1) { + // goog.math.randomInt is inlined to reduce dependencies. + var j = Math.floor(Math.random() * i); // exclusive + i--; + var tmp = this.tests_[i]; + this.tests_[i] = this.tests_[j]; + this.tests_[j] = tmp; + } + break; + + case goog.testing.TestCase.Order.SORTED: + this.tests_.sort(function(t1, t2) { + if (t1.name == t2.name) { + return 0; + } + return t1.name < t2.name ? -1 : 1; + }); + break; + + // Do nothing for NATURAL. + } +}; + + +/** + * Gets list of objects that potentially contain test cases. For IE 8 and + * below, this is the global "this" (for properties set directly on the global + * this or window) and the RuntimeObject (for global variables and functions). + * For all other browsers, the array simply contains the global this. + * + * @param {string=} opt_prefix An optional prefix. If specified, only get things + * under this prefix. Note that the prefix is only honored in IE, since it + * supports the RuntimeObject: + * http://msdn.microsoft.com/en-us/library/ff521039%28VS.85%29.aspx + * TODO: Remove this option. + * @return {!Array} A list of objects that should be inspected. + */ +goog.testing.TestCase.prototype.getGlobals = function(opt_prefix) { + return goog.testing.TestCase.getGlobals(opt_prefix); +}; + + +/** + * Gets list of objects that potentially contain test cases. For IE 8 and + * below, this is the global "this" (for properties set directly on the global + * this or window) and the RuntimeObject (for global variables and functions). + * For all other browsers, the array simply contains the global this. + * + * @param {string=} opt_prefix An optional prefix. If specified, only get things + * under this prefix. Note that the prefix is only honored in IE, since it + * supports the RuntimeObject: + * http://msdn.microsoft.com/en-us/library/ff521039%28VS.85%29.aspx + * TODO: Remove this option. + * @return {!Array} A list of objects that should be inspected. + */ +goog.testing.TestCase.getGlobals = function(opt_prefix) { + // Look in the global scope for most browsers, on IE we use the little known + // RuntimeObject which holds references to all globals. We reference this + // via goog.global so that there isn't an aliasing that throws an exception + // in Firefox. + return typeof goog.global['RuntimeObject'] != 'undefined' ? + [goog.global['RuntimeObject']((opt_prefix || '') + '*'), goog.global] : + [goog.global]; +}; + + +/** + * @private {?goog.testing.TestCase} + */ +goog.testing.TestCase.activeTestCase_ = null; + + +/** + * @return {?goog.testing.TestCase} currently active test case or null if not + * test is currently running. Tries the G_testRunner first then the stored + * value (when run outside of G_testRunner. + */ +goog.testing.TestCase.getActiveTestCase = function() { + var gTestRunner = goog.global['G_testRunner']; + if (gTestRunner && gTestRunner.testCase) { + return gTestRunner.testCase; + } else { + return goog.testing.TestCase.activeTestCase_; + } +}; + + +/** + * Calls {@link goog.testing.TestCase.prototype.invalidateAssertionException} + * on the active test case if it is installed, and logs an error otherwise. + * @param {!goog.testing.JsUnitException} e The exception object to invalidate. + * @package + */ +goog.testing.TestCase.invalidateAssertionException = function(e) { + var testCase = goog.testing.TestCase.getActiveTestCase(); + if (testCase) { + testCase.invalidateAssertionException(e); + } else { + goog.global.console.error( + 'Failed to remove expected exception: no test case is installed.'); + } +}; + + +/** + * Gets called before any tests are executed. Can be overridden to set up the + * environment for the whole test case. + * @return {!Thenable|undefined} + */ +goog.testing.TestCase.prototype.setUpPage = function() {}; + + +/** + * Gets called after all tests have been executed. Can be overridden to tear + * down the entire test case. + */ +goog.testing.TestCase.prototype.tearDownPage = function() {}; + + +/** + * Gets called before every goog.testing.TestCase.Test is been executed. Can + * be overridden to add set up functionality to each test. + * @return {!Thenable|undefined} + */ +goog.testing.TestCase.prototype.setUp = function() {}; + + +/** + * Gets called after every goog.testing.TestCase.Test has been executed. Can + * be overridden to add tear down functionality to each test. + * @return {!Thenable|undefined} + */ +goog.testing.TestCase.prototype.tearDown = function() {}; + + +/** + * @return {string} The function name prefix used to auto-discover tests. + */ +goog.testing.TestCase.prototype.getAutoDiscoveryPrefix = function() { + return 'test'; +}; + + +/** + * @return {number} Time since the last batch of tests was started. + * @protected + */ +goog.testing.TestCase.prototype.getBatchTime = function() { + return this.batchTime_; +}; + + +/** + * @param {number} batchTime Time since the last batch of tests was started. + * @protected + */ +goog.testing.TestCase.prototype.setBatchTime = function(batchTime) { + this.batchTime_ = batchTime; +}; + + +/** + * Creates a `goog.testing.TestCase.Test` from an auto-discovered + * function. + * @param {string} name The name of the function. + * @param {function()} ref The auto-discovered function. + * @param {!Object=} scope The scope to attach to the test. + * @param {!Array=} objChain + * @return {!goog.testing.TestCase.Test} The newly created test. + * @protected + */ +goog.testing.TestCase.prototype.createTest = function( + name, ref, scope, objChain) { + return new goog.testing.TestCase.Test(name, ref, scope, objChain); +}; + + +/** + * Adds any functions defined on the global object + * that correspond to lifecycle events for the test case. Overrides + * setUp, tearDown, setUpPage, tearDownPage, runTests, and shouldRunTests + * if they are defined on global object. + */ +goog.testing.TestCase.prototype.autoDiscoverLifecycle = function() { + this.setLifecycleObj(goog.global); +}; + + +// TODO(johnlenz): make this package private +/** + * Extracts any functions defined on 'obj' that correspond to page lifecycle + * events (setUpPage, tearDownPage, runTests, shouldRunTests) and add them to + * on this test case. + * @param {!Object} obj + */ +goog.testing.TestCase.prototype.setLifecycleObj = function(obj) { + if (obj['setUp']) { + this.setUp = goog.bind(obj['setUp'], obj); + } + if (obj['tearDown']) { + this.tearDown = goog.bind(obj['tearDown'], obj); + } + if (obj['setUpPage']) { + this.setUpPage = goog.bind(obj['setUpPage'], obj); + } + if (obj['tearDownPage']) { + this.tearDownPage = goog.bind(obj['tearDownPage'], obj); + } + if (obj['runTests']) { + this.runTests = goog.bind(obj['runTests'], obj); + } + if (obj['shouldRunTests']) { + this.shouldRunTests = goog.bind(obj['shouldRunTests'], obj); + } +}; + + +// TODO(johnlenz): make this package private +/** + * @param {!Object} obj An object from which to extract test and lifecycle + * methods. + */ +goog.testing.TestCase.prototype.setTestObj = function(obj) { + // Check any previously added (likely auto-discovered) tests, only one source + // of discovered test and life-cycle methods is allowed. + goog.asserts.assert( + this.tests_.length == 0, 'Test methods have already been configured.'); + this.shouldAutoDiscoverTests_ = false; + if (obj['getTestName']) { + this.name_ = obj['getTestName'](); + } + this.setLifecycleObj(obj); + this.addTestObj_(obj, '', [this]); +}; + +/** + * @param {!Object} obj An object from which to extract test and lifecycle + * methods. + * @param {string} name + * @param {!Array} objChain List of objects that have methods used + * to create tests such as setUp, tearDown. + * @private + */ +goog.testing.TestCase.prototype.addTestObj_ = function(obj, name, objChain) { + var regex = new RegExp('^' + this.getAutoDiscoveryPrefix()); + var properties = goog.object.getAllPropertyNames(obj); + for (var i = 0; i < properties.length; i++) { + var testName = properties[i]; + if (regex.test(testName)) { + var testProperty; + try { + testProperty = obj[testName]; + } catch (ex) { + // NOTE(brenneman): When running tests from a file:// URL on Firefox + // 3.5 for Windows, any reference to goog.global.sessionStorage raises + // an "Operation is not supported" exception. Ignore any exceptions + // raised by simply accessing global properties. + testProperty = null; + } + if (name) { + testName = testName.slice(this.getAutoDiscoveryPrefix().length); + } + var fullTestName = name + (testName && name ? '_' : '') + testName; + if (goog.isFunction(testProperty)) { + this.addNewTest(fullTestName, testProperty, obj, objChain); + } else if (goog.isObject(testProperty)) { + // To prevent infinite loops. + if (!goog.array.contains(objChain, testProperty)) { + var newObjChain = objChain.slice(); + newObjChain.push(testProperty); + this.addTestObj_(testProperty, fullTestName, newObjChain); + } + } + } + } +}; + + +/** + * Adds any functions defined in the global scope that are prefixed with + * "test" to the test case. + */ +goog.testing.TestCase.prototype.autoDiscoverTests = function() { + this.autoDiscoverLifecycle(); + var prefix = this.getAutoDiscoveryPrefix(); + var testSources = this.getGlobals(prefix); + + for (var i = 0; i < testSources.length; i++) { + var testSource = testSources[i]; + this.addTestObj_(testSource, '', [this]); + } + + this.orderTests_(); +}; + + +/** + * Checks to see if the test should be marked as failed before it is run. + * + * If there was an error in setUpPage, we treat that as a failure for all + * tests and mark them all as having failed. + * + * @param {goog.testing.TestCase.Test} testCase The current test case. + * @return {boolean} Whether the test was marked as failed. + * @protected + */ +goog.testing.TestCase.prototype.maybeFailTestEarly = function(testCase) { + if (this.exceptionBeforeTest) { + // We just use the first error to report an error on a failed test. + testCase.name = 'setUpPage for ' + testCase.name; + this.recordError(testCase.name, this.exceptionBeforeTest); + return true; + } + return false; +}; + + +/** + * Cycles through the tests, yielding asynchronously if the execution time + * exceeds {@link #maxRunTime}. In particular, there is no guarantee that + * the test case has finished execution once this method has returned. + * To be notified when the test case has finished execution, use + * {@link #addCompletedCallback} or {@link #runTestsReturningPromise}. + */ +goog.testing.TestCase.prototype.cycleTests = function() { + this.saveMessage('Start'); + this.batchTime_ = this.now(); + if (this.running) { + this.runNextTestCallback_ = goog.nullFunction; + // Kick off the tests. runNextTest_ will schedule all of the tests, + // using a mixture of synchronous and asynchronous strategies. + goog.testing.Continuation_.run(this.runNextTest_()); + } +}; + + +/** + * Counts the number of files that were loaded for dependencies that are + * required to run the test. + * @return {number} The number of files loaded. + * @private + */ +goog.testing.TestCase.prototype.countNumFilesLoaded_ = function() { + var scripts = goog.dom.getElementsByTagName(goog.dom.TagName.SCRIPT); + var count = 0; + for (var i = 0, n = scripts.length; i < n; i++) { + if (scripts[i].src) { + count++; + } + } + return count; +}; + + +/** + * Calls a function after a delay, using the protected timeout. + * @param {Function} fn The function to call. + * @param {number} time Delay in milliseconds. + * @return {number} The timeout id. + * @protected + */ +goog.testing.TestCase.prototype.timeout = function(fn, time) { + // NOTE: invoking protectedSetTimeout_ as a member of goog.testing.TestCase + // would result in an Illegal Invocation error. The method must be executed + // with the global context. + var protectedSetTimeout = goog.testing.TestCase.protectedSetTimeout_; + return protectedSetTimeout(fn, time); +}; + + +/** + * Clears a timeout created by `this.timeout()`. + * @param {number} id A timeout id. + * @protected + */ +goog.testing.TestCase.prototype.clearTimeout = function(id) { + // NOTE: see execution note for protectedSetTimeout above. + var protectedClearTimeout = goog.testing.TestCase.protectedClearTimeout_; + protectedClearTimeout(id); +}; + + +/** + * @return {number} The current time in milliseconds. + * @protected + */ +goog.testing.TestCase.prototype.now = function() { + return goog.testing.TestCase.now(); +}; + + +/** + * @return {number} The current time in milliseconds. + * @protected + */ +goog.testing.TestCase.now = function() { + // don't use goog.now as some tests override it. + if (goog.testing.TestCase.protectedPerformance_) { + return goog.testing.TestCase.protectedPerformance_.now(); + } + // Fallback for IE8 + // Cannot use "new goog.testing.TestCase.protectedDate_()" due to b/8323223. + var protectedDate = goog.testing.TestCase.protectedDate_; + return new protectedDate().getTime(); +}; + + +/** + * Returns the current time. + * @return {string} HH:MM:SS. + * @private + */ +goog.testing.TestCase.prototype.getTimeStamp_ = function() { + // Cannot use "new goog.testing.TestCase.protectedDate_()" due to b/8323223. + var protectedDate = goog.testing.TestCase.protectedDate_; + var d = new protectedDate(); + + // Ensure millis are always 3-digits + var millis = '00' + d.getMilliseconds(); + millis = millis.substr(millis.length - 3); + + return this.pad_(d.getHours()) + ':' + this.pad_(d.getMinutes()) + ':' + + this.pad_(d.getSeconds()) + '.' + millis; +}; + + +/** + * Pads a number to make it have a leading zero if it's less than 10. + * @param {number} number The number to pad. + * @return {string} The resulting string. + * @private + */ +goog.testing.TestCase.prototype.pad_ = function(number) { + return number < 10 ? '0' + number : String(number); +}; + + +/** + * Trims a path to be only that after google3. + * @param {string} path The path to trim. + * @return {string} The resulting string. + * @private + */ +goog.testing.TestCase.prototype.trimPath_ = function(path) { + return path.substring(path.indexOf('google3') + 8); +}; + + +/** + * Handles a test that passed. + * @param {goog.testing.TestCase.Test} test The test that passed. + * @protected + */ +goog.testing.TestCase.prototype.doSuccess = function(test) { + this.result_.successCount++; + // An empty list of error messages indicates that the test passed. + // If we already have a failure for this test, do not set to empty list. + if (!(test.name in this.result_.resultsByName)) { + this.result_.resultsByName[test.name] = []; + } + var message = test.name + ' : PASSED'; + this.saveMessage(message); + this.log(message); + if (this.testDone_) { + this.doTestDone_(test, []); + } +}; + + +/** + * Handles a test that was skipped. + * @param {!goog.testing.TestCase.Test} test The test that was skipped. + * @protected + */ +goog.testing.TestCase.prototype.doSkipped = function(test) { + this.result_.skipCount++; + // An empty list of error messages indicates that the test passed. + // If we already have a failure for this test, do not set to empty list. + if (!(test.name in this.result_.resultsByName)) { + this.result_.resultsByName[test.name] = []; + } + var message = test.name + ' : SKIPPED'; + this.saveMessage(message); + this.log(message); + if (this.testDone_) { + this.doTestDone_(test, []); + } +}; + + +/** + * Records an error that fails the current test, without throwing it. + * + * Use this function to implement expect()-style assertion libraries that fail a + * test without breaking execution (so you can see further failures). Do not use + * this from normal test code. + * + * Please contact js-core-libraries-team@ before using this method. If it grows + * popular, we may add an expect() API to Closure. + * + * NOTE: If there is no active TestCase, you must throw an error. + * @param {!Error} error The error to log. If it is a JsUnitException which has + * already been logged, nothing will happen. + */ +goog.testing.TestCase.prototype.recordTestError = function(error) { + this.recordError( + this.curTest_ ? this.curTest_.name : '', error); +}; + + + +/** + * Records and logs an error from or related to a test. + * @param {string} testName The name of the test that failed. + * @param {*} error The exception object associated with the + * failure or a string. + * @protected + */ +goog.testing.TestCase.prototype.recordError = function(testName, error) { + if (error && error['isJsUnitException'] && error['loggedJsUnitException']) { + // We already logged this error; don't record it again. This is particularly + // important for errors from mocks, which are rethrown by $verify, called by + // tearDown(). + return; + } + + var err = this.logError(testName, error); + this.result_.errors.push(err); + if (testName in this.result_.resultsByName) { + this.result_.resultsByName[testName].push(err); + } else { + this.result_.resultsByName[testName] = [err]; + } + + if (error && error['isJsUnitException']) { + error['loggedJsUnitException'] = true; + } +}; + + +/** + * Handles a test that failed. + * @param {goog.testing.TestCase.Test} test The test that failed. + * @protected + */ +goog.testing.TestCase.prototype.doError = function(test) { + var message = test.name + ' : FAILED'; + this.log(message); + this.saveMessage(message); + + if (this.testDone_) { + var results = this.result_.resultsByName[test.name]; + var errMsgs = []; + for (var i = 0; i < results.length; i++) { + errMsgs.push(results[i].toString()); + } + this.doTestDone_(test, errMsgs); + } +}; + + +/** + * Makes note of an exception arising from an assertion, and then throws it. + * If the test otherwise passes (i.e., because something else caught the + * exception on its way to the test framework), it will be forced to fail. + * @param {!goog.testing.JsUnitException} e The exception object being thrown. + * @throws {goog.testing.JsUnitException} + * @package + */ +goog.testing.TestCase.prototype.raiseAssertionException = function(e) { + if (this.failOnUnreportedAsserts) { + this.thrownAssertionExceptions_.push(e); + } + throw e; +}; + + +/** + * Removes the specified exception from being tracked. This only needs to be + * called for internal functions that intentionally catch an exception, such + * as + * `#assertThrowsJsUnitException`. + * @param {!goog.testing.JsUnitException} e The exception object to invalidate. + * @package + */ +goog.testing.TestCase.prototype.invalidateAssertionException = function(e) { + if (this.failOnUnreportedAsserts) { + goog.array.remove(this.thrownAssertionExceptions_, e); + } +}; + + +/** + * @param {string} name Failed test name. + * @param {*} error The exception object associated with the + * failure or a string. + * @return {!goog.testing.TestCase.Error} Error object. + * @suppress {missingProperties} message and stack properties + */ +goog.testing.TestCase.prototype.logError = function(name, error) { + var errMsg = null; + var stack = null; + if (error) { + this.log(error); + if (goog.isString(error)) { + errMsg = error; + } else { + errMsg = error.message || error.description || error.toString(); + stack = error.stack ? error.stack : error['stackTrace']; + } + } else { + errMsg = 'An unknown error occurred'; + } + + if (stack) { + // The Error class includes the message in the stack. Don't duplicate it. + stack = stack.replace('Error: ' + errMsg + '\n', 'Error\n'); + + // Remove extra goog.testing.TestCase frames from the end. + stack = stack.replace( + /\n\s*(\bat\b)?\s*(goog\.labs\.testing\.EnvironmentTestCase_\.)?goog\.testing\.(Continuation_\.(prototype\.)?run|TestCase\.(prototype\.)?(execute|cycleTests|startNextBatch_|safeRunTest_|invokeFunction_?))[^\0]*/m, + ''); + } + var err = new goog.testing.TestCase.Error(name, errMsg, stack); + + this.saveMessage(err.toString()); + + return err; +}; + +/** + * A class representing a single test function. + * @param {string} name The test name. + * @param {?function()} ref Reference to the test function or test object. + * @param {?Object=} scope Optional scope that the test function should be + * called in. + * @param {!Array=} objChain A chain of objects used to populate setUps + * and tearDowns. + * @constructor + */ +goog.testing.TestCase.Test = function(name, ref, scope, objChain) { + /** + * The name of the test. + * @type {string} + */ + this.name = name; + + /** + * TODO(user): Rename this to something more clear. + * Reference to the test function. + * @type {function()} + */ + this.ref = ref || function() {}; + + /** + * Scope that the test function should be called in. + * @type {?Object} + */ + this.scope = scope || null; + + /** + * @type {!Array} + */ + this.setUps = []; + + /** + * @type {!Array} + */ + this.tearDowns = []; + + /** + * @type {!Array} + */ + this.objChain = objChain || []; + + if (objChain) { + for (var i = 0; i < objChain.length; i++) { + if (goog.isFunction(objChain[i].setUp)) { + this.setUps.push(goog.bind(objChain[i].setUp, objChain[i])); + } + if (goog.isFunction(objChain[i].tearDown)) { + this.tearDowns.push(goog.bind(objChain[i].tearDown, objChain[i])); + } + } + this.tearDowns.reverse(); + } + + /** + * Timestamp just before the test begins execution. + * @type {number} + * @private + */ + this.startTime_; + + /** + * Timestamp just after the test ends execution. + * @type {number} + * @private + */ + this.stoppedTime_; + + /** @package {boolean|undefined} */ + this.waiting; +}; + +/** + * Executes the test function. + * @package + */ +goog.testing.TestCase.Test.prototype.execute = function() { + this.ref.call(this.scope); +}; + +/** + * Sets the start time + */ +goog.testing.TestCase.Test.prototype.started = function() { + this.startTime_ = goog.testing.TestCase.now(); +}; + +/** + * Sets the stop time + */ +goog.testing.TestCase.Test.prototype.stopped = function() { + this.stoppedTime_ = goog.testing.TestCase.now(); +}; + +/** + * Returns the runtime for this test function + * @return {number} milliseconds takenn by the test. + */ +goog.testing.TestCase.Test.prototype.getElapsedTime = function() { + return this.stoppedTime_ - this.startTime_; +}; + +/** + * A class for representing test results. A bag of public properties. + * @param {goog.testing.TestCase} testCase The test case that owns this result. + * @constructor + * @final + */ +goog.testing.TestCase.Result = function(testCase) { + /** + * The test case that owns this result. + * @type {goog.testing.TestCase} + * @private + */ + this.testCase_ = testCase; + + /** + * Total number of tests that should have been run. + * @type {number} + */ + this.totalCount = 0; + + /** + * Total number of tests that were actually run. + * @type {number} + */ + this.runCount = 0; + + /** + * Number of successful tests. + * @type {number} + */ + this.successCount = 0; + + /** + * Number of tests skipped due to nested shouldRunTests. + * @type {number} + */ + this.skipCount = 0; + + /** + * The amount of time the tests took to run. + * @type {number} + */ + this.runTime = 0; + + /** + * The number of files loaded to run this test. + * @type {number} + */ + this.numFilesLoaded = 0; + + /** + * Whether all tests were suppressed from a top-level shouldRunTests(). + * @type {boolean} + */ + this.testSuppressed = false; + + /** + * Which tests were suppressed by shouldRunTests() returning false. + * @type {!Array} + */ + this.suppressedTests = []; + + /** + * Test results for each test that was run. The test name is always added + * as the key in the map, and the array of strings is an optional list + * of failure messages. If the array is empty, the test passed. Otherwise, + * the test failed. + * @type {!Object>} + */ + this.resultsByName = {}; + + /** + * Errors encountered while running the test. + * @type {!Array} + */ + this.errors = []; + + /** + * Messages to show the user after running the test. + * @type {!Array} + */ + this.messages = []; + + /** + * Whether the tests have completed. + * @type {boolean} + */ + this.complete = false; +}; + + +/** + * @return {boolean} Whether the test was successful. + */ +goog.testing.TestCase.Result.prototype.isSuccess = function() { + return this.complete && this.errors.length == 0; +}; + + +/** + * @return {string} A summary of the tests, including total number of tests that + * passed, failed, and the time taken. + */ +goog.testing.TestCase.Result.prototype.getSummary = function() { + var summary = this.runCount + ' of ' + this.totalCount + ' tests run in ' + + this.runTime + 'ms.\n'; + if (this.testSuppressed) { + summary += 'Tests not run because shouldRunTests() returned false.'; + } else { + var failures = this.totalCount - this.successCount - this.skipCount; + var suppressionMessage = ''; + + if (this.skipCount) { + suppressionMessage += + ', ' + this.skipCount + ' skipped by shouldRunTests()'; + } + + var countOfRunTests = this.testCase_.getActuallyRunCount(); + if (countOfRunTests) { + failures = countOfRunTests - this.successCount - this.skipCount; + suppressionMessage += ', ' + (this.totalCount - countOfRunTests) + + ' suppressed by querystring'; + } + summary += this.successCount + ' passed, ' + failures + ' failed' + + suppressionMessage + '.\n' + Math.round(this.runTime / this.runCount) + + ' ms/test. ' + this.numFilesLoaded + ' files loaded.'; + } + + return summary; +}; + + +/** + * @param {function(goog.testing.TestCase.Test, !Array)} testDone + */ +goog.testing.TestCase.prototype.setTestDoneCallback = function(testDone) { + this.testDone_ = testDone; +}; + + +/** + * @param {goog.testing.TestCase.Test} test + * @param {!Array} errMsgs + * @private + */ +goog.testing.TestCase.prototype.doTestDone_ = function(test, errMsgs) { + test.stopped(); + this.testDone_(test, errMsgs); +}; + +/** + * Initializes the TestCase. + * @param {goog.testing.TestCase} testCase The test case to install. + * @param {function(goog.testing.TestCase.Test, Array)=} opt_testDone + * Called when each test completes. + */ +goog.testing.TestCase.initializeTestCase = function(testCase, opt_testDone) { + if (opt_testDone) { + testCase.setTestDoneCallback(opt_testDone); + } + + if (testCase.shouldAutoDiscoverTests_) { + testCase.autoDiscoverTests(); + } else { + // Make sure the tests are still ordered based on provided order. + testCase.orderTests_(); + } + + if (goog.global.location) { + var search = goog.global.location.search; + testCase.setTestsToRun(goog.testing.TestCase.parseRunTests_(search)); + } + goog.testing.TestCase.activeTestCase_ = testCase; +}; + + +/** + * Initializes the given test case with the global test runner 'G_testRunner'. + * @param {goog.testing.TestCase} testCase The test case to install. + * @param {function(goog.testing.TestCase.Test, Array)=} opt_testDone + * Called when each test completes. + */ +goog.testing.TestCase.initializeTestRunner = function(testCase, opt_testDone) { + goog.testing.TestCase.initializeTestCase(testCase, opt_testDone); + + var gTestRunner = goog.global['G_testRunner']; + if (gTestRunner) { + gTestRunner['initialize'](testCase); + } else { + throw new Error( + 'G_testRunner is undefined. Please ensure goog.testing.jsunit' + + ' is included.'); + } +}; + + +/** + * Parses URL query parameters for the 'runTests' parameter. + * @param {string} search The URL query string. + * @return {Object} A set of test names or test indices to be + * run by the test runner. + * @private + */ +goog.testing.TestCase.parseRunTests_ = function(search) { + var testsToRun = null; + var runTestsMatch = search.match(/(?:\?|&)runTests=([^?&]+)/i); + if (runTestsMatch) { + testsToRun = {}; + var arr = runTestsMatch[1].split(','); + for (var i = 0, len = arr.length; i < len; i++) { + testsToRun[arr[i]] = true; + } + } + return testsToRun; +}; + + +/** + * Wraps provided promise and returns a new promise which will be rejected + * if the original promise does not settle within the given timeout. + * @param {!goog.Promise} promise + * @param {number} timeoutInMs Number of milliseconds to wait for the promise to + * settle before failing it with a timeout error. + * @param {string} errorMsg Error message to use if the promise times out. + * @return {!goog.Promise} A promise that will settle with the original + promise unless the timeout is exceeded. + * error. + * @template T + * @private + */ +goog.testing.TestCase.prototype.rejectIfPromiseTimesOut_ = function( + promise, timeoutInMs, errorMsg) { + var self = this; + var start = this.now(); + return new goog.Promise(function(resolve, reject) { + var timeoutId = self.timeout(function() { + var elapsed = self.now() - start; + reject(new Error(errorMsg + '\nElapsed time: ' + elapsed + 'ms.')); + }, timeoutInMs); + promise.then(resolve, reject); + var clearTimeout = goog.bind(self.clearTimeout, self, timeoutId); + promise.then(clearTimeout, clearTimeout); + }); +}; + + + +/** + * A class representing an error thrown by the test + * @param {string} source The name of the test which threw the error. + * @param {string} message The error message. + * @param {string=} opt_stack A string showing the execution stack. + * @constructor + * @final + */ +goog.testing.TestCase.Error = function(source, message, opt_stack) { + /** + * The name of the test which threw the error. + * @type {string} + */ + this.source = source; + + /** + * Reference to the test function. + * @type {string} + */ + this.message = message; + + /** + * The stack. + * @type {?string} + */ + this.stack = null; + + if (opt_stack) { + this.stack = opt_stack; + } else { + // Attempt to capture a stack trace. + if (Error.captureStackTrace) { + // See https://code.google.com/p/v8-wiki/wiki/JavaScriptStackTraceApi + Error.captureStackTrace(this, goog.testing.TestCase.Error); + } else { + var stack = new Error().stack; + if (stack) { + this.stack = stack; + } + } + } +}; + + +/** + * Returns a string representing the error object. + * @return {string} A string representation of the error. + * @override + */ +goog.testing.TestCase.Error.prototype.toString = function() { + return 'ERROR in ' + this.source + '\n' + this.message + + (this.stack ? '\n' + this.stack : ''); +}; + +/** + * Returns an object representing the error suitable for JSON serialization. + * @return {!goog.testing.TestCase.IResult} An object + * representation of the error. + * @private + */ +goog.testing.TestCase.Error.prototype.toObject_ = function() { + return { + 'source': this.source, + 'message': this.message, + 'stacktrace': this.stack || '' + }; +}; + + + +/** + * @constructor + * @param {function(): (?goog.testing.Continuation_|undefined)} fn + * @private + */ +goog.testing.Continuation_ = function(fn) { + /** @private @const */ + this.fn_ = fn; +}; + + +/** @param {?goog.testing.Continuation_|undefined} continuation */ +goog.testing.Continuation_.run = function(continuation) { + var fn = continuation && continuation.fn_; + while (fn) { + continuation = fn(); + fn = continuation && continuation.fn_; + } +}; diff --git a/closure-library/closure/goog/testing/testqueue.js b/closure-library/closure/goog/testing/testqueue.js new file mode 100644 index 0000000000..02f24fba4d --- /dev/null +++ b/closure-library/closure/goog/testing/testqueue.js @@ -0,0 +1,67 @@ +// Copyright 2007 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 Generic queue for writing unit tests. + */ + +goog.setTestOnly('goog.testing.TestQueue'); +goog.provide('goog.testing.TestQueue'); + + + +/** + * Generic queue for writing unit tests + * @constructor + */ +goog.testing.TestQueue = function() { + /** + * Events that have accumulated + * @type {Array} + * @private + */ + this.events_ = []; +}; + + +/** + * Adds a new event onto the queue. + * @param {Object} event The event to queue. + */ +goog.testing.TestQueue.prototype.enqueue = function(event) { + this.events_.push(event); +}; + + +/** + * Returns whether the queue is empty. + * @return {boolean} Whether the queue is empty. + */ +goog.testing.TestQueue.prototype.isEmpty = function() { + return this.events_.length == 0; +}; + + +/** + * Gets the next event from the queue. Throws an exception if the queue is + * empty. + * @param {string=} opt_comment Comment if the queue is empty. + * @return {Object} The next event from the queue. + */ +goog.testing.TestQueue.prototype.dequeue = function(opt_comment) { + if (this.isEmpty()) { + throw new Error('Handler is empty: ' + opt_comment); + } + return this.events_.shift(); +}; diff --git a/closure-library/closure/goog/testing/testrunner.js b/closure-library/closure/goog/testing/testrunner.js new file mode 100644 index 0000000000..0debb1b620 --- /dev/null +++ b/closure-library/closure/goog/testing/testrunner.js @@ -0,0 +1,537 @@ +// Copyright 2007 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 The test runner is a singleton object that is used to execute + * a goog.testing.TestCases, display the results, and expose the results to + * Selenium for automation. If a TestCase hasn't been registered with the + * runner by the time window.onload occurs, the testRunner will try to auto- + * discover JsUnit style test pages. + * + * The hooks for selenium are (see http://go/selenium-hook-setup):- + * - Boolean G_testRunner.isFinished() + * - Boolean G_testRunner.isSuccess() + * - String G_testRunner.getReport() + * - number G_testRunner.getRunTime() + * - Object> G_testRunner.getTestResults() + * + * Testing code should not have dependencies outside of goog.testing so as to + * reduce the chance of masking missing dependencies. + * + */ + +goog.setTestOnly('goog.testing.TestRunner'); +goog.provide('goog.testing.TestRunner'); + +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.safe'); +goog.require('goog.json'); +goog.require('goog.testing.TestCase'); +goog.require('goog.userAgent'); + + + +/** + * Construct a test runner. + * + * NOTE(user): This is currently pretty weird, I'm essentially trying to + * create a wrapper that the Selenium test can hook into to query the state of + * the running test case, while making goog.testing.TestCase general. + * + * @constructor + */ +goog.testing.TestRunner = function() { + /** + * Errors that occurred in the window. + * @type {!Array} + */ + this.errors = []; + + /** + * Reference to the active test case. + * @type {?goog.testing.TestCase} + */ + this.testCase = null; + + /** + * Whether the test runner has been initialized yet. + * @type {boolean} + */ + this.initialized = false; + + /** + * Element created in the document to add test results to. + * @private {?Element} + */ + this.logEl_ = null; + + /** + * Function to use when filtering errors. + * @private {(function(string))?} + */ + this.errorFilter_ = null; + + /** + * Whether an empty test case counts as an error. + * @private {boolean} + */ + this.strict_ = true; + + /** + * Store the serializer to avoid it being overwritten by a mock. + * @private {function(!Object): string} + */ + this.jsonStringify_ = goog.json.serialize; + + /** + * An id unique to this runner. Checked by the server during polling to + * verify that the page was not reloaded. + * @private {string} + */ + this.uniqueId_ = ((Math.random() * 1e9) >>> 0) + '-' + + window.location.pathname.replace(/.*\//, '').replace(/\.html.*$/, ''); + + if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher(11)) { + return; + } + + var self = this; + function onPageHide() { + self.clearUniqueId(); + } + window.addEventListener('pagehide', onPageHide); + +}; + +/** + * The uuid is embedded in the URL search. This function allows us to mock + * the search in the test. + * @return {string} + */ +goog.testing.TestRunner.prototype.getSearchString = function() { + return window.location.search; +}; + +/** + * Returns the unique id for this test page. + * @return {string} + */ +goog.testing.TestRunner.prototype.getUniqueId = function() { + return this.uniqueId_; +}; + +/** + * Clears the unique id for this page. The value will hint the reason. + */ +goog.testing.TestRunner.prototype.clearUniqueId = function() { + this.uniqueId_ = 'pagehide'; +}; + +/** + * Initializes the test runner. + * @param {goog.testing.TestCase} testCase The test case to initialize with. + */ +goog.testing.TestRunner.prototype.initialize = function(testCase) { + if (this.testCase && this.testCase.running) { + throw new Error( + 'The test runner is already waiting for a test to complete'); + } + this.testCase = testCase; + this.initialized = true; +}; + + +/** + * By default, the test runner is strict, and fails if it runs an empty + * test case. + * @param {boolean} strict Whether the test runner should fail on an empty + * test case. + */ +goog.testing.TestRunner.prototype.setStrict = function(strict) { + this.strict_ = strict; +}; + + +/** + * @return {boolean} Whether the test runner should fail on an empty + * test case. + */ +goog.testing.TestRunner.prototype.isStrict = function() { + return this.strict_; +}; + + +/** + * Returns true if the test runner is initialized. + * Used by Selenium Hooks. + * @return {boolean} Whether the test runner is active. + */ +goog.testing.TestRunner.prototype.isInitialized = function() { + return this.initialized; +}; + + +/** + * Returns false if the test runner has not finished successfully. + * Used by Selenium Hooks. + * @return {boolean} Whether the test runner is not active. + */ +goog.testing.TestRunner.prototype.isFinished = function() { + return this.errors.length > 0 || this.isComplete(); +}; + + +/** + * Returns true if the test runner is finished. + * @return {boolean} True if the test runner started and subsequently completed. + */ +goog.testing.TestRunner.prototype.isComplete = function() { + return this.initialized && !!this.testCase && this.testCase.started && + !this.testCase.running; +}; + +/** + * Returns true if the test case didn't fail. + * Used by Selenium Hooks. + * @return {boolean} Whether the current test returned successfully. + */ +goog.testing.TestRunner.prototype.isSuccess = function() { + return !this.hasErrors() && !!this.testCase && this.testCase.isSuccess(); +}; + + +/** + * Returns true if the test case runner has errors that were caught outside of + * the test case. + * @return {boolean} Whether there were JS errors. + */ +goog.testing.TestRunner.prototype.hasErrors = function() { + return this.errors.length > 0; +}; + + +/** + * Logs an error that occurred. Used in the case of environment setting up + * an onerror handler. + * @param {string} msg Error message. + */ +goog.testing.TestRunner.prototype.logError = function(msg) { + if (this.isComplete()) { + // Once the user has checked their code, subsequent errors can occur + // because of tearDown actions. For now, log these but do not fail the test. + this.log('Error after test completed: ' + msg); + return; + } + if (!this.errorFilter_ || this.errorFilter_.call(null, msg)) { + this.errors.push(msg); + } +}; + + +/** + * Log failure in current running test. + * @param {Error} ex Exception. + */ +goog.testing.TestRunner.prototype.logTestFailure = function(ex) { + var testName = /** @type {string} */ (goog.testing.TestCase.currentTestName); + if (this.testCase) { + this.testCase.logError(testName, ex); + } else { + // NOTE: Do not forget to log the original exception raised. + throw new Error( + 'Test runner not initialized with a test case. Original ' + + 'exception: ' + ex.message); + } +}; + + +/** + * Sets a function to use as a filter for errors. + * @param {function(string)} fn Filter function. + */ +goog.testing.TestRunner.prototype.setErrorFilter = function(fn) { + this.errorFilter_ = fn; +}; + + +/** + * Returns a report of the test case that ran. + * Used by Selenium Hooks. + * @param {boolean=} opt_verbose If true results will include data about all + * tests, not just what failed. + * @return {string} A report summary of the test. + */ +goog.testing.TestRunner.prototype.getReport = function(opt_verbose) { + var report = []; + if (this.testCase) { + report.push(this.testCase.getReport(opt_verbose)); + } + if (this.errors.length > 0) { + report.push('JavaScript errors detected by test runner:'); + report.push.apply(report, this.errors); + report.push('\n'); + } + return report.join('\n'); +}; + + +/** + * Returns the amount of time it took for the test to run. + * Used by Selenium Hooks. + * @return {number} The run time, in milliseconds. + */ +goog.testing.TestRunner.prototype.getRunTime = function() { + return this.testCase ? this.testCase.getRunTime() : 0; +}; + + +/** + * Returns the number of script files that were loaded in order to run the test. + * @return {number} The number of script files. + */ +goog.testing.TestRunner.prototype.getNumFilesLoaded = function() { + return this.testCase ? this.testCase.getNumFilesLoaded() : 0; +}; + + +/** + * Executes a test case and prints the results to the window. + */ +goog.testing.TestRunner.prototype.execute = function() { + if (!this.testCase) { + throw new Error( + 'The test runner must be initialized with a test case ' + + 'before execute can be called.'); + } + + if (this.strict_ && this.testCase.getCount() == 0) { + throw new Error( + 'No tests found in given test case: ' + this.testCase.getName() + '. ' + + 'By default, the test runner fails if a test case has no tests. ' + + 'To modify this behavior, see goog.testing.TestRunner\'s ' + + 'setStrict() method, or G_testRunner.setStrict()'); + } + + this.testCase.addCompletedCallback(goog.bind(this.onComplete_, this)); + if (goog.testing.TestRunner.shouldUsePromises_(this.testCase)) { + this.testCase.runTestsReturningPromise(); + } else { + this.testCase.runTests(); + } +}; + + +/** + * @param {!goog.testing.TestCase} testCase + * @return {boolean} + * @private + */ +goog.testing.TestRunner.shouldUsePromises_ = function(testCase) { + return testCase.constructor === goog.testing.TestCase; +}; + + +/** @const {string} The ID of the element to log output to. */ +goog.testing.TestRunner.TEST_LOG_ID = 'closureTestRunnerLog'; + + +/** + * Writes the results to the document when the test case completes. + * @private + */ +goog.testing.TestRunner.prototype.onComplete_ = function() { + var log = this.testCase.getReport(true); + if (this.errors.length > 0) { + log += '\n' + this.errors.join('\n'); + } + + if (!this.logEl_) { + var el = document.getElementById(goog.testing.TestRunner.TEST_LOG_ID); + if (el == null) { + el = goog.dom.createElement(goog.dom.TagName.DIV); + el.id = goog.testing.TestRunner.TEST_LOG_ID; + document.body.appendChild(el); + } + this.logEl_ = el; + } + + // Highlight the page to indicate the overall outcome. + this.writeLog(log); + + // TODO(chrishenry): Make this work with multiple test cases (b/8603638). + var runAgainLink = goog.dom.createElement(goog.dom.TagName.A); + runAgainLink.style.display = 'inline-block'; + runAgainLink.style.fontSize = 'small'; + runAgainLink.style.marginBottom = '16px'; + runAgainLink.href = ''; + runAgainLink.onclick = goog.bind(function() { + this.execute(); + return false; + }, this); + runAgainLink.innerHTML = 'Run again without reloading'; + this.logEl_.appendChild(runAgainLink); +}; + + +/** + * Writes a nicely formatted log out to the document. + * @param {string} log The string to write. + */ +goog.testing.TestRunner.prototype.writeLog = function(log) { + var lines = log.split('\n'); + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + var color; + var isPassed = /PASSED/.test(line); + var isSkipped = /SKIPPED/.test(line); + var isFailOrError = + /FAILED/.test(line) || /ERROR/.test(line) || /NO TESTS RUN/.test(line); + if (isPassed) { + color = 'darkgreen'; + } else if (isSkipped) { + color = 'slategray'; + } else if (isFailOrError) { + color = 'darkred'; + } else { + color = '#333'; + } + var div = goog.dom.createElement(goog.dom.TagName.DIV); + if (line.substr(0, 2) == '> ') { + // The stack trace may contain links so it has to be interpreted as HTML. + div.innerHTML = line; + } else { + div.appendChild(document.createTextNode(line)); + } + + var testNameMatch = /(\S+) (\[[^\]]*] )?: (FAILED|ERROR|PASSED)/.exec(line); + if (testNameMatch) { + // Build a URL to run the test individually. If this test was already + // part of another subset test, we need to overwrite the old runTests + // query parameter. We also need to do this without bringing in any + // extra dependencies, otherwise we could mask missing dependency bugs. + var newSearch = 'runTests=' + testNameMatch[1]; + var search = window.location.search; + if (search) { + var oldTests = /runTests=([^&]*)/.exec(search); + if (oldTests) { + newSearch = search.substr(0, oldTests.index) + newSearch + + search.substr(oldTests.index + oldTests[0].length); + } else { + newSearch = search + '&' + newSearch; + } + } else { + newSearch = '?' + newSearch; + } + var href = window.location.href; + var hash = window.location.hash; + if (hash && hash.charAt(0) != '#') { + hash = '#' + hash; + } + href = href.split('#')[0].split('?')[0] + newSearch + hash; + + // Add the link. + var a = goog.dom.createElement(goog.dom.TagName.A); + a.innerHTML = '(run individually)'; + a.style.fontSize = '0.8em'; + a.style.color = '#888'; + goog.dom.safe.setAnchorHref(a, href); + div.appendChild(document.createTextNode(' ')); + div.appendChild(a); + } + + div.style.color = color; + div.style.font = 'normal 100% monospace'; + div.style.wordWrap = 'break-word'; + if (i == 0) { + // Highlight the first line as a header that indicates the test outcome. + div.style.padding = '20px'; + div.style.marginBottom = '10px'; + if (isPassed) { + div.style.border = '1px solid ' + color; + div.style.backgroundColor = '#eeffee'; + } else if (isFailOrError) { + div.style.border = '5px solid ' + color; + div.style.backgroundColor = '#ffeeee'; + } else { + div.style.border = '1px solid black'; + div.style.backgroundColor = '#eeeeee'; + } + } + + try { + div.style.whiteSpace = 'pre-wrap'; + } catch (e) { + // NOTE(brenneman): IE raises an exception when assigning to pre-wrap. + // Thankfully, it doesn't collapse whitespace when using monospace fonts, + // so it will display correctly if we ignore the exception. + } + + if (i < 2) { + div.style.fontWeight = 'bold'; + } + this.logEl_.appendChild(div); + } +}; + + +/** + * Logs a message to the current test case. + * @param {string} s The text to output to the log. + */ +goog.testing.TestRunner.prototype.log = function(s) { + if (this.testCase) { + this.testCase.log(s); + } +}; + + +// TODO(nnaze): Properly handle serving test results when multiple test cases +// are run. +/** + * @return {Object>} A map of + * test names to a list of test failures (if any) to provide formatted data + * for the test runner. + */ +goog.testing.TestRunner.prototype.getTestResults = function() { + if (this.testCase) { + return this.testCase.getTestResults(); + } + return null; +}; + + +/** + * Returns the test results as json. + * This is called by the testing infrastructure through G_testrunner. + * @return {?string} Tests results object. + */ +goog.testing.TestRunner.prototype.getTestResultsAsJson = function() { + if (this.testCase) { + var testCaseResults + /** {Object>} */ + = this.testCase.getTestResults(); + if (this.hasErrors()) { + var globalErrors = []; + for (var i = 0; i < this.errors.length; i++) { + globalErrors.push( + {source: '', message: this.errors[i], stacktrace: ''}); + } + // We are writing on our testCase results, but the test is over. + testCaseResults['globalErrors'] = globalErrors; + } + return this.jsonStringify_(testCaseResults); + } + return null; +}; diff --git a/closure-library/closure/goog/testing/testsuite.js b/closure-library/closure/goog/testing/testsuite.js new file mode 100644 index 0000000000..f4df0500d8 --- /dev/null +++ b/closure-library/closure/goog/testing/testsuite.js @@ -0,0 +1,67 @@ +// 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.setTestOnly('goog.testing.testSuite'); +goog.provide('goog.testing.testSuite'); + +goog.require('goog.labs.testing.Environment'); +goog.require('goog.testing.TestCase'); + +/** + * @typedef {{order: (!goog.testing.TestCase.Order|undefined)}} + */ +var TestSuiteOptions; + +/** + * Runs the lifecycle methods (setUp, tearDown, etc.) and test* methods from + * the given object. For use in tests that are written as JavaScript modules + * or goog.modules. + * + * @param {!Object} obj An object with one or more + * test methods, and optional setUp, tearDown and getTestName methods. The + * object may also have nested Objects (named like tests, i.e. + * `testNestedSuite: {}`) that will be treated as nested testSuites. Any + * additional setUp will run after parent setUps, any additional tearDown + * will run before parent tearDowns. The this object refers to the object + * that the functions were defined on, not the full testSuite object. + * @param {!TestSuiteOptions=} opt_options Optional options object which can + * be used to set the sort order for running tests. + */ +goog.testing.testSuite = function(obj, opt_options) { + if (goog.isFunction(obj)) { + throw new Error( + 'testSuite should be called with an object. ' + + 'Did you forget to initialize a class?'); + } + + if (goog.testing.testSuite.initialized_) { + throw new Error('Only one TestSuite can be active'); + } + goog.testing.testSuite.initialized_ = true; + + var testCase = goog.labs.testing.Environment.getTestCaseIfActive() || + new goog.testing.TestCase(document.title); + testCase.setTestObj(obj); + + var options = opt_options || {}; + if (options.order) { + testCase.setOrder(options.order); + } + goog.testing.TestCase.initializeTestRunner(testCase); +}; + +/** + * True iff the testSuite has been created. + * @private {boolean} + */ +goog.testing.testSuite.initialized_ = false; diff --git a/closure-library/closure/goog/testing/ui/rendererasserts.js b/closure-library/closure/goog/testing/ui/rendererasserts.js new file mode 100644 index 0000000000..c9dfc0a612 --- /dev/null +++ b/closure-library/closure/goog/testing/ui/rendererasserts.js @@ -0,0 +1,59 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Additional asserts for testing ControlRenderers. + * + * @author mkretzschmar@google.com (Martin Kretzschmar) + */ + +goog.setTestOnly('goog.testing.ui.rendererasserts'); +goog.provide('goog.testing.ui.rendererasserts'); + +goog.require('goog.testing.asserts'); +goog.require('goog.ui.ControlRenderer'); + + +/** + * Assert that a control renderer constructor doesn't call getCssClass. + * + * @param {function(new:goog.ui.ControlRenderer)} rendererClassUnderTest The + * renderer constructor to test. + */ +goog.testing.ui.rendererasserts.assertNoGetCssClassCallsInConstructor = + function(rendererClassUnderTest) { + var getCssClassCalls = 0; + + /** + * @constructor + * @extends {goog.ui.ControlRenderer} + * @final + */ + function TestControlRenderer() { rendererClassUnderTest.call(this); } + goog.inherits(TestControlRenderer, rendererClassUnderTest); + + /** @override */ + TestControlRenderer.prototype.getCssClass = function() { + getCssClassCalls++; + return TestControlRenderer.superClass_.getCssClass.call(this); + }; + + // Looking for the side-effects caused by the construction here: + new TestControlRenderer(); + + assertEquals( + 'Constructors should not call getCssClass, ' + + 'getCustomRenderer must be able to override it post construction.', + 0, getCssClassCalls); +}; diff --git a/closure-library/closure/goog/testing/ui/rendererharness.js b/closure-library/closure/goog/testing/ui/rendererharness.js new file mode 100644 index 0000000000..c7268ea7c6 --- /dev/null +++ b/closure-library/closure/goog/testing/ui/rendererharness.js @@ -0,0 +1,183 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// All Rights Reserved + +/** + * @fileoverview A driver for testing renderers. + * + * @author nicksantos@google.com (Nick Santos) + */ + +goog.setTestOnly('goog.testing.ui.RendererHarness'); +goog.provide('goog.testing.ui.RendererHarness'); + +goog.require('goog.Disposable'); +goog.require('goog.dom.NodeType'); +goog.require('goog.testing.asserts'); +goog.require('goog.testing.dom'); +goog.require('goog.ui.Control'); +goog.require('goog.ui.ControlRenderer'); + + + +/** + * A driver for testing renderers. + * + * @param {goog.ui.ControlRenderer} renderer A renderer to test. + * @param {Element} renderParent The parent of the element where controls will + * be rendered. + * @param {Element} decorateParent The parent of the element where controls will + * be decorated. + * @constructor + * @extends {goog.Disposable} + * @final + */ +goog.testing.ui.RendererHarness = function( + renderer, renderParent, decorateParent) { + goog.Disposable.call(this); + + /** + * The renderer under test. + * @type {goog.ui.ControlRenderer} + * @private + */ + this.renderer_ = renderer; + + /** + * The parent of the element where controls will be rendered. + * @type {Element} + * @private + */ + this.renderParent_ = renderParent; + + /** + * The original HTML of the render element. + * @type {string} + * @private + */ + this.renderHtml_ = renderParent.innerHTML; + + /** + * The parent of the element where controls will be decorated. + * @type {Element} + * @private + */ + this.decorateParent_ = decorateParent; + + /** + * The original HTML of the decorated element. + * @type {string} + * @private + */ + this.decorateHtml_ = decorateParent.innerHTML; +}; +goog.inherits(goog.testing.ui.RendererHarness, goog.Disposable); + + +/** + * A control to create by decoration. + * @type {goog.ui.Control} + * @private + */ +goog.testing.ui.RendererHarness.prototype.decorateControl_; + + +/** + * A control to create by rendering. + * @type {goog.ui.Control} + * @private + */ +goog.testing.ui.RendererHarness.prototype.renderControl_; + + +/** + * Whether all the necessary assert methods have been called. + * @type {boolean} + * @private + */ +goog.testing.ui.RendererHarness.prototype.verified_ = false; + + +/** + * Attach a control and render its DOM. + * @param {goog.ui.Control} control A control. + * @return {Element} The element created. + */ +goog.testing.ui.RendererHarness.prototype.attachControlAndRender = function( + control) { + this.renderControl_ = control; + + control.setRenderer(this.renderer_); + control.render(this.renderParent_); + return control.getElement(); +}; + + +/** + * Attach a control and decorate the element given in the constructor. + * @param {goog.ui.Control} control A control. + * @return {Element} The element created. + */ +goog.testing.ui.RendererHarness.prototype.attachControlAndDecorate = function( + control) { + this.decorateControl_ = control; + + control.setRenderer(this.renderer_); + + var child = this.decorateParent_.firstChild; + assertEquals( + 'The decorated node must be an element', goog.dom.NodeType.ELEMENT, + child.nodeType); + control.decorate(/** @type {!Element} */ (child)); + return control.getElement(); +}; + + +/** + * Assert that the rendered element and the decorated element match. + */ +goog.testing.ui.RendererHarness.prototype.assertDomMatches = function() { + assert( + 'Both elements were not generated', + !!(this.renderControl_ && this.decorateControl_)); + goog.testing.dom.assertHtmlMatches( + this.renderControl_.getElement().innerHTML, + this.decorateControl_.getElement().innerHTML); + this.verified_ = true; +}; + + +/** + * Destroy the harness, verifying that all assertions had been checked. + * @override + * @protected + */ +goog.testing.ui.RendererHarness.prototype.disposeInternal = function() { + // If the harness was not verified appropriately, throw an exception. + assert( + 'Expected assertDomMatches to be called', + this.verified_ || !this.renderControl_ || !this.decorateControl_); + + if (this.decorateControl_) { + this.decorateControl_.dispose(); + } + if (this.renderControl_) { + this.renderControl_.dispose(); + } + + this.renderParent_.innerHTML = this.renderHtml_; + this.decorateParent_.innerHTML = this.decorateHtml_; + + goog.testing.ui.RendererHarness.superClass_.disposeInternal.call(this); +}; diff --git a/closure-library/closure/goog/testing/ui/style.js b/closure-library/closure/goog/testing/ui/style.js new file mode 100644 index 0000000000..465880c6c2 --- /dev/null +++ b/closure-library/closure/goog/testing/ui/style.js @@ -0,0 +1,146 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Tools for testing Closure renderers against static markup + * spec pages. + * + */ + +goog.setTestOnly('goog.testing.ui.style'); +goog.provide('goog.testing.ui.style'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.classlist'); +goog.require('goog.testing.asserts'); + + +/** + * Uses document.write to add an iFrame to the page with the reference path in + * the src attribute. Used for loading an html file containing reference + * structures to test against into the page. Should be called within the body of + * the jsunit test page. + * @param {string} referencePath A path to a reference HTML file. + */ +goog.testing.ui.style.writeReferenceFrame = function(referencePath) { + document.write( + ''); +}; + + +/** + * Returns a reference to the first element child of a node with the given id + * from the page loaded into the reference iFrame. Used to retrieve a particular + * reference DOM structure to test against. + * @param {string} referenceId The id of a container element for a reference + * structure in the reference page. + * @return {Node} The root element of the reference structure. + */ +goog.testing.ui.style.getReferenceNode = function(referenceId) { + return goog.dom.getFirstElementChild( + window.frames['reference'].document.getElementById(referenceId)); +}; + + +/** + * Returns an array of all element children of a given node. + * @param {Node} element The node to get element children of. + * @return {!Array} An array of all the element children. + */ +goog.testing.ui.style.getElementChildren = function(element) { + var first = goog.dom.getFirstElementChild(element); + if (!first) { + return []; + } + var children = [first], next; + while (next = goog.dom.getNextElementSibling(children[children.length - 1])) { + children.push(next); + } + return children; +}; + + +/** + * Tests whether a given node is a "content" node of a reference structure, + * which means it is allowed to have arbitrary children. + * @param {Node} element The node to test. + * @return {boolean} Whether the given node is a content node or not. + * @suppress {missingProperties} "className" not defined on Node + */ +goog.testing.ui.style.isContentNode = function(element) { + return element.className.indexOf('content') != -1; +}; + + +/** + * Tests that the structure, node names, and classes of the given element are + * the same as the reference structure with the given id. Throws an error if the + * element doesn't have the same nodes at each level of the DOM with the same + * classes on each. The test ignores all DOM structure within content nodes. + * @param {Node} element The root node of the DOM structure to test. + * @param {string} referenceId The id of the container for the reference + * structure to test against. + */ +goog.testing.ui.style.assertStructureMatchesReference = function( + element, referenceId) { + goog.testing.ui.style.assertStructureMatchesReferenceInner_( + element, goog.testing.ui.style.getReferenceNode(referenceId)); +}; + + +/** + * A recursive function for comparing structure, node names, and classes between + * a test and reference DOM structure. Throws an error if one of these things + * doesn't match. Used internally by + * {@link goog.testing.ui.style.assertStructureMatchesReference}. + * @param {Node} element DOM element to test. + * @param {Node} reference DOM element to use as a reference (test against). + * @private + */ +goog.testing.ui.style.assertStructureMatchesReferenceInner_ = function( + element, reference) { + if (!element && !reference) { + return; + } + assertTrue('Expected two elements.', !!element && !!reference); + assertEquals( + 'Expected nodes to have the same nodeName.', element.nodeName, + reference.nodeName); + var testElem = goog.asserts.assertElement(element); + var refElem = goog.asserts.assertElement(reference); + var elementClasses = goog.dom.classlist.get(testElem); + goog.array.forEach(goog.dom.classlist.get(refElem), function(referenceClass) { + assertContains( + 'Expected test node to have all reference classes.', referenceClass, + elementClasses); + }); + // Call assertStructureMatchesReferenceInner_ on all element children + // unless this is a content node + var elChildren = goog.testing.ui.style.getElementChildren(element), + refChildren = goog.testing.ui.style.getElementChildren(reference); + if (!goog.testing.ui.style.isContentNode(reference)) { + if (elChildren.length != refChildren.length) { + assertEquals( + 'Expected same number of children for a non-content node.', + elChildren.length, refChildren.length); + } + for (var i = 0; i < elChildren.length; i++) { + goog.testing.ui.style.assertStructureMatchesReferenceInner_( + elChildren[i], refChildren[i]); + } + } +}; diff --git a/closure-library/closure/goog/timer/timer.js b/closure-library/closure/goog/timer/timer.js new file mode 100644 index 0000000000..1baf6a2f72 --- /dev/null +++ b/closure-library/closure/goog/timer/timer.js @@ -0,0 +1,325 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A timer class to which other classes and objects can listen on. + * This is only an abstraction above `setInterval`. + * + * @see ../demos/timers.html + */ + +goog.provide('goog.Timer'); + +goog.require('goog.Promise'); +goog.require('goog.events.EventTarget'); + + + +/** + * Class for handling timing events. + * + * @param {number=} opt_interval Number of ms between ticks (default: 1ms). + * @param {Object=} opt_timerObject An object that has `setTimeout`, + * `setInterval`, `clearTimeout` and `clearInterval` + * (e.g., `window`). + * @constructor + * @extends {goog.events.EventTarget} + */ +goog.Timer = function(opt_interval, opt_timerObject) { + goog.events.EventTarget.call(this); + + /** + * Number of ms between ticks + * @private {number} + */ + this.interval_ = opt_interval || 1; + + /** + * An object that implements `setTimeout`, `setInterval`, + * `clearTimeout` and `clearInterval`. We default to the window + * object. Changing this on {@link goog.Timer.prototype} changes the object + * for all timer instances which can be useful if your environment has some + * other implementation of timers than the `window` object. + * @private {{setTimeout:!Function, clearTimeout:!Function}} + */ + this.timerObject_ = /** @type {{setTimeout, clearTimeout}} */ ( + opt_timerObject || goog.Timer.defaultTimerObject); + + /** + * Cached `tick_` bound to the object for later use in the timer. + * @private {Function} + * @const + */ + this.boundTick_ = goog.bind(this.tick_, this); + + /** + * Firefox browser often fires the timer event sooner (sometimes MUCH sooner) + * than the requested timeout. So we compare the time to when the event was + * last fired, and reschedule if appropriate. See also + * {@link goog.Timer.intervalScale}. + * @private {number} + */ + this.last_ = goog.now(); +}; +goog.inherits(goog.Timer, goog.events.EventTarget); + + +/** + * Maximum timeout value. + * + * Timeout values too big to fit into a signed 32-bit integer may cause overflow + * in FF, Safari, and Chrome, resulting in the timeout being scheduled + * immediately. It makes more sense simply not to schedule these timeouts, since + * 24.8 days is beyond a reasonable expectation for the browser to stay open. + * + * @private {number} + * @const + */ +goog.Timer.MAX_TIMEOUT_ = 2147483647; + + +/** + * A timer ID that cannot be returned by any known implementation of + * `window.setTimeout`. Passing this value to `window.clearTimeout` + * should therefore be a no-op. + * + * @private {number} + * @const + */ +goog.Timer.INVALID_TIMEOUT_ID_ = -1; + + +/** + * Whether this timer is enabled + * @type {boolean} + */ +goog.Timer.prototype.enabled = false; + + +/** + * An object that implements `setTimeout`, `setInterval`, + * `clearTimeout` and `clearInterval`. We default to the global + * object. Changing `goog.Timer.defaultTimerObject` changes the object for + * all timer instances which can be useful if your environment has some other + * implementation of timers you'd like to use. + * @type {{setTimeout, clearTimeout}} + */ +goog.Timer.defaultTimerObject = goog.global; + + +/** + * Variable that controls the timer error correction. If the timer is called + * before the requested interval times `intervalScale`, which often + * happens on Mozilla, the timer is rescheduled. + * @see {@link #last_} + * @type {number} + */ +goog.Timer.intervalScale = 0.8; + + +/** + * Variable for storing the result of `setInterval`. + * @private {?number} + */ +goog.Timer.prototype.timer_ = null; + + +/** + * Gets the interval of the timer. + * @return {number} interval Number of ms between ticks. + */ +goog.Timer.prototype.getInterval = function() { + return this.interval_; +}; + + +/** + * Sets the interval of the timer. + * @param {number} interval Number of ms between ticks. + */ +goog.Timer.prototype.setInterval = function(interval) { + this.interval_ = interval; + if (this.timer_ && this.enabled) { + // Stop and then start the timer to reset the interval. + this.stop(); + this.start(); + } else if (this.timer_) { + this.stop(); + } +}; + + +/** + * Callback for the `setTimeout` used by the timer. + * @private + */ +goog.Timer.prototype.tick_ = function() { + if (this.enabled) { + var elapsed = goog.now() - this.last_; + if (elapsed > 0 && elapsed < this.interval_ * goog.Timer.intervalScale) { + this.timer_ = this.timerObject_.setTimeout( + this.boundTick_, this.interval_ - elapsed); + return; + } + + // Prevents setInterval from registering a duplicate timeout when called + // in the timer event handler. + if (this.timer_) { + this.timerObject_.clearTimeout(this.timer_); + this.timer_ = null; + } + + this.dispatchTick(); + // The timer could be stopped in the timer event handler. + if (this.enabled) { + // Stop and start to ensure there is always only one timeout even if + // start is called in the timer event handler. + this.stop(); + this.start(); + } + } +}; + + +/** + * Dispatches the TICK event. This is its own method so subclasses can override. + */ +goog.Timer.prototype.dispatchTick = function() { + this.dispatchEvent(goog.Timer.TICK); +}; + + +/** + * Starts the timer. + */ +goog.Timer.prototype.start = function() { + this.enabled = true; + + // If there is no interval already registered, start it now + if (!this.timer_) { + // IMPORTANT! + // window.setInterval in FireFox has a bug - it fires based on + // absolute time, rather than on relative time. What this means + // is that if a computer is sleeping/hibernating for 24 hours + // and the timer interval was configured to fire every 1000ms, + // then after the PC wakes up the timer will fire, in rapid + // succession, 3600*24 times. + // This bug is described here and is already fixed, but it will + // take time to propagate, so for now I am switching this over + // to setTimeout logic. + // https://bugzilla.mozilla.org/show_bug.cgi?id=376643 + // + this.timer_ = this.timerObject_.setTimeout(this.boundTick_, this.interval_); + this.last_ = goog.now(); + } +}; + + +/** + * Stops the timer. + */ +goog.Timer.prototype.stop = function() { + this.enabled = false; + if (this.timer_) { + this.timerObject_.clearTimeout(this.timer_); + this.timer_ = null; + } +}; + + +/** @override */ +goog.Timer.prototype.disposeInternal = function() { + goog.Timer.superClass_.disposeInternal.call(this); + this.stop(); + delete this.timerObject_; +}; + + +/** + * Constant for the timer's event type. + * @const + */ +goog.Timer.TICK = 'tick'; + + +/** + * Calls the given function once, after the optional pause. + *

      + * The function is always called asynchronously, even if the delay is 0. This + * is a common trick to schedule a function to run after a batch of browser + * event processing. + * + * @param {function(this:SCOPE)|{handleEvent:function()}|null} listener Function + * or object that has a handleEvent method. + * @param {number=} opt_delay Milliseconds to wait; default is 0. + * @param {SCOPE=} opt_handler Object in whose scope to call the listener. + * @return {number} A handle to the timer ID. + * @template SCOPE + */ +goog.Timer.callOnce = function(listener, opt_delay, opt_handler) { + if (goog.isFunction(listener)) { + if (opt_handler) { + listener = goog.bind(listener, opt_handler); + } + } else if (listener && typeof listener.handleEvent == 'function') { + // using typeof to prevent strict js warning + listener = goog.bind(listener.handleEvent, listener); + } else { + throw new Error('Invalid listener argument'); + } + + if (Number(opt_delay) > goog.Timer.MAX_TIMEOUT_) { + // Timeouts greater than MAX_INT return immediately due to integer + // overflow in many browsers. Since MAX_INT is 24.8 days, just don't + // schedule anything at all. + return goog.Timer.INVALID_TIMEOUT_ID_; + } else { + return goog.Timer.defaultTimerObject.setTimeout(listener, opt_delay || 0); + } +}; + + +/** + * Clears a timeout initiated by {@link #callOnce}. + * @param {?number} timerId A timer ID. + */ +goog.Timer.clear = function(timerId) { + goog.Timer.defaultTimerObject.clearTimeout(timerId); +}; + + +/** + * @param {number} delay Milliseconds to wait. + * @param {(RESULT|goog.Thenable|Thenable)=} opt_result The value + * with which the promise will be resolved. + * @return {!goog.Promise} A promise that will be resolved after + * the specified delay, unless it is canceled first. + * @template RESULT + */ +goog.Timer.promise = function(delay, opt_result) { + var timerKey = null; + return new goog + .Promise(function(resolve, reject) { + timerKey = + goog.Timer.callOnce(function() { resolve(opt_result); }, delay); + if (timerKey == goog.Timer.INVALID_TIMEOUT_ID_) { + reject(new Error('Failed to schedule timer.')); + } + }) + .thenCatch(function(error) { + // Clear the timer. The most likely reason is "cancel" signal. + goog.Timer.clear(timerKey); + throw error; + }); +}; diff --git a/closure-library/closure/goog/transitionalforwarddeclarations.js b/closure-library/closure/goog/transitionalforwarddeclarations.js new file mode 100644 index 0000000000..45822ebed3 --- /dev/null +++ b/closure-library/closure/goog/transitionalforwarddeclarations.js @@ -0,0 +1,32 @@ +// Copyright 2017 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 The forward declarations in this file are here to faciliate + * the removal of "deps.js" from the "base" rule. These types are + * included in various extern files. These rules should be cleaned up + * so that these declarations aren't necessary. + * @suppress {extraRequire} + */ + +goog.forwardDeclare('goog.Promise'); +goog.forwardDeclare('goog.date.DateLike'); +goog.forwardDeclare('goog.date.DateTime'); +goog.forwardDeclare('goog.events.EventId'); +goog.forwardDeclare('goog.events.Key'); +goog.forwardDeclare('goog.events.KeyCodes'); +goog.forwardDeclare('goog.i18n.TimeZone'); +goog.forwardDeclare('goog.math.Range'); +goog.forwardDeclare('goog.math.Size'); +goog.forwardDeclare('goog.structs.Map'); diff --git a/closure-library/closure/goog/transpile.js b/closure-library/closure/goog/transpile.js new file mode 100644 index 0000000000..0148f254d1 --- /dev/null +++ b/closure-library/closure/goog/transpile.js @@ -0,0 +1,1958 @@ +// Copyright 2016 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. + +// NOTE: This is a generated file. Do not edit. + +// clang-format off + +/** @fileoverview @nocompile */ +var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.ASSUME_ES5=false;$jscomp.ASSUME_NO_NATIVE_MAP=false;$jscomp.ASSUME_NO_NATIVE_SET=false;$jscomp.SIMPLE_FROUND_POLYFILL=false;$jscomp.defineProperty=$jscomp.ASSUME_ES5||typeof Object.defineProperties=="function"?Object.defineProperty:function(target,property,descriptor){descriptor=descriptor;if(target==Array.prototype||target==Object.prototype)return;target[property]=descriptor.value}; +$jscomp.getGlobal=function(maybeGlobal){return typeof window!="undefined"&&window===maybeGlobal?maybeGlobal:typeof global!="undefined"&&global!=null?global:maybeGlobal};$jscomp.global=$jscomp.getGlobal(this); +$jscomp.polyfill=function(target,polyfill,fromLang,toLang){if(!polyfill)return;var obj=$jscomp.global;var split=target.split(".");for(var i=0;ifrom)if(--final in this)this[--to]=this[final];else delete this[--to]}return this};function toInteger(arg){var n=Number(arg);if(n===Infinity||n===-Infinity)return n;return n|0}return polyfill},"es6","es3");$jscomp.arrayIteratorImpl=function(array){var index=0;return function(){if(indexlength)opt_end=length;opt_end=Number(opt_end);if(opt_end<0)opt_end=Math.max(0,length+opt_end);for(var i=Number(opt_start||0);i0){var inner=Array.prototype.flat.call(element,depth-1);flattened.push.apply(flattened,inner)}else flattened.push(element)}return flattened};return flat},"es9","es5"); +$jscomp.polyfill("Array.prototype.flatMap",function(orig){if(orig)return orig;var flatMap=function(callback,thisArg){var mapped=[];for(var i=0;i0){var property=this.properties_.pop();if(property in this.object_)return property}return null};$jscomp.generator.Engine_=function(program){this.context_=new $jscomp.generator.Context;this.program_=program}; +$jscomp.generator.Engine_.prototype.next_=function(value){this.context_.start_();if(this.context_.yieldAllIterator_)return this.yieldAllStep_(this.context_.yieldAllIterator_.next,value,this.context_.next_);this.context_.next_(value);return this.nextStep_()}; +$jscomp.generator.Engine_.prototype.return_=function(value){this.context_.start_();var yieldAllIterator=this.context_.yieldAllIterator_;if(yieldAllIterator){var returnFunction="return"in yieldAllIterator?yieldAllIterator["return"]:function(v){return{value:v,done:true}};return this.yieldAllStep_(returnFunction,value,this.context_["return"])}this.context_["return"](value);return this.nextStep_()}; +$jscomp.generator.Engine_.prototype.throw_=function(exception){this.context_.start_();if(this.context_.yieldAllIterator_)return this.yieldAllStep_(this.context_.yieldAllIterator_["throw"],exception,this.context_.next_);this.context_.throw_(exception);return this.nextStep_()}; +$jscomp.generator.Engine_.prototype.yieldAllStep_=function(action,value,nextAction){try{var result=action.call(this.context_.yieldAllIterator_,value);$jscomp.generator.ensureIteratorResultIsObject_(result);if(!result.done){this.context_.stop_();return result}var resultValue=result.value}catch(e){this.context_.yieldAllIterator_=null;this.context_.throw_(e);return this.nextStep_()}this.context_.yieldAllIterator_=null;nextAction.call(this.context_,resultValue);return this.nextStep_()}; +$jscomp.generator.Engine_.prototype.nextStep_=function(){while(this.context_.nextAddress)try{var yieldValue=this.program_(this.context_);if(yieldValue){this.context_.stop_();return{value:yieldValue.value,done:false}}}catch(e){this.context_.yieldResult=undefined;this.context_.throw_(e)}this.context_.stop_();if(this.context_.abruptCompletion_){var abruptCompletion=this.context_.abruptCompletion_;this.context_.abruptCompletion_=null;if(abruptCompletion.isException)throw abruptCompletion.exception;return{value:abruptCompletion["return"], +done:true}}return{value:undefined,done:true}};$jscomp.generator.Generator_=function(engine){this.next=function(opt_value){return engine.next_(opt_value)};this["throw"]=function(exception){return engine.throw_(exception)};this["return"]=function(value){return engine.return_(value)};$jscomp.initSymbolIterator();this[Symbol.iterator]=function(){return this}}; +$jscomp.generator.createGenerator=function(generator,program){var result=new $jscomp.generator.Generator_(new $jscomp.generator.Engine_(program));if($jscomp.setPrototypeOf)$jscomp.setPrototypeOf(result,generator.prototype);return result}; +$jscomp.asyncExecutePromiseGenerator=function(generator){function passValueToGenerator(value){return generator.next(value)}function passErrorToGenerator(error){return generator["throw"](error)}return new Promise(function(resolve,reject){function handleGeneratorRecord(genRec){if(genRec.done)resolve(genRec.value);else Promise.resolve(genRec.value).then(passValueToGenerator,passErrorToGenerator).then(handleGeneratorRecord,reject)}handleGeneratorRecord(generator.next())})}; +$jscomp.asyncExecutePromiseGeneratorFunction=function(generatorFunction){return $jscomp.asyncExecutePromiseGenerator(generatorFunction())};$jscomp.asyncExecutePromiseGeneratorProgram=function(program){return $jscomp.asyncExecutePromiseGenerator(new $jscomp.generator.Generator_(new $jscomp.generator.Engine_(program)))}; +$jscomp.checkEs6ConformanceViaProxy=function(){try{var proxied={};var proxy=Object.create(new $jscomp.global["Proxy"](proxied,{"get":function(target,key,receiver){return target==proxied&&key=="q"&&receiver==proxy}}));return proxy["q"]===true}catch(err){return false}};$jscomp.USE_PROXY_FOR_ES6_CONFORMANCE_CHECKS=false;$jscomp.ES6_CONFORMANCE=$jscomp.USE_PROXY_FOR_ES6_CONFORMANCE_CHECKS&&$jscomp.checkEs6ConformanceViaProxy(); +$jscomp.owns=function(obj,prop){return Object.prototype.hasOwnProperty.call(obj,prop)}; +$jscomp.polyfill("WeakMap",function(NativeWeakMap){function isConformant(){if(!NativeWeakMap||!Object.seal)return false;try{var x=Object.seal({});var y=Object.seal({});var map=new NativeWeakMap([[x,2],[y,3]]);if(map.get(x)!=2||map.get(y)!=3)return false;map["delete"](x);map.set(y,4);return!map.has(x)&&map.get(y)==4}catch(err){return false}}if($jscomp.USE_PROXY_FOR_ES6_CONFORMANCE_CHECKS){if(NativeWeakMap&&$jscomp.ES6_CONFORMANCE)return NativeWeakMap}else if(isConformant())return NativeWeakMap;var prop= +"$jscomp_hidden_"+Math.random();function WeakMapMembership(){}function insert(target){if(!$jscomp.owns(target,prop)){var obj=new WeakMapMembership;$jscomp.defineProperty(target,prop,{value:obj})}}function patch(name){var prev=Object[name];if(prev)Object[name]=function(target){if(target instanceof WeakMapMembership)return target;else{insert(target);return prev(target)}}}patch("freeze");patch("preventExtensions");patch("seal");var index=0;var PolyfillWeakMap=function(opt_iterable){this.id_=(index+= +Math.random()+1).toString();if(opt_iterable){var iter=$jscomp.makeIterator(opt_iterable);var entry;while(!(entry=iter.next()).done){var item=entry.value;this.set(item[0],item[1])}}};PolyfillWeakMap.prototype.set=function(key,value){insert(key);if(!$jscomp.owns(key,prop))throw new Error("WeakMap key fail: "+key);key[prop][this.id_]=value;return this};PolyfillWeakMap.prototype.get=function(key){return $jscomp.owns(key,prop)?key[prop][this.id_]:undefined};PolyfillWeakMap.prototype.has=function(key){return $jscomp.owns(key, +prop)&&$jscomp.owns(key[prop],this.id_)};PolyfillWeakMap.prototype["delete"]=function(key){if(!$jscomp.owns(key,prop)||!$jscomp.owns(key[prop],this.id_))return false;return delete key[prop][this.id_]};return PolyfillWeakMap},"es6","es3");$jscomp.MapEntry=function(){this.previous;this.next;this.head;this.key;this.value}; +$jscomp.polyfill("Map",function(NativeMap){function isConformant(){if($jscomp.ASSUME_NO_NATIVE_MAP||!NativeMap||typeof NativeMap!="function"||!NativeMap.prototype.entries||typeof Object.seal!="function")return false;try{NativeMap=NativeMap;var key=Object.seal({x:4});var map=new NativeMap($jscomp.makeIterator([[key,"s"]]));if(map.get(key)!="s"||map.size!=1||map.get({x:4})||map.set({x:4},"t")!=map||map.size!=2)return false;var iter=map.entries();var item=iter.next();if(item.done||item.value[0]!=key|| +item.value[1]!="s")return false;item=iter.next();if(item.done||item.value[0].x!=4||item.value[1]!="t"||!iter.next().done)return false;return true}catch(err){return false}}if($jscomp.USE_PROXY_FOR_ES6_CONFORMANCE_CHECKS){if(NativeMap&&$jscomp.ES6_CONFORMANCE)return NativeMap}else if(isConformant())return NativeMap;$jscomp.initSymbolIterator();var idMap=new WeakMap;var PolyfillMap=function(opt_iterable){this.data_={};this.head_=createHead();this.size=0;if(opt_iterable){var iter=$jscomp.makeIterator(opt_iterable); +var entry;while(!(entry=iter.next()).done){var item=entry.value;this.set(item[0],item[1])}}};PolyfillMap.prototype.set=function(key,value){key=key===0?0:key;var r=maybeGetEntry(this,key);if(!r.list)r.list=this.data_[r.id]=[];if(!r.entry){r.entry={next:this.head_,previous:this.head_.previous,head:this.head_,key:key,value:value};r.list.push(r.entry);this.head_.previous.next=r.entry;this.head_.previous=r.entry;this.size++}else r.entry.value=value;return this};PolyfillMap.prototype["delete"]=function(key){var r= +maybeGetEntry(this,key);if(r.entry&&r.list){r.list.splice(r.index,1);if(!r.list.length)delete this.data_[r.id];r.entry.previous.next=r.entry.next;r.entry.next.previous=r.entry.previous;r.entry.head=null;this.size--;return true}return false};PolyfillMap.prototype.clear=function(){this.data_={};this.head_=this.head_.previous=createHead();this.size=0};PolyfillMap.prototype.has=function(key){return!!maybeGetEntry(this,key).entry};PolyfillMap.prototype.get=function(key){var entry=maybeGetEntry(this,key).entry; +return entry&&entry.value};PolyfillMap.prototype.entries=function(){return makeIterator(this,function(entry){return[entry.key,entry.value]})};PolyfillMap.prototype.keys=function(){return makeIterator(this,function(entry){return entry.key})};PolyfillMap.prototype.values=function(){return makeIterator(this,function(entry){return entry.value})};PolyfillMap.prototype.forEach=function(callback,opt_thisArg){var iter=this.entries();var item;while(!(item=iter.next()).done){var entry=item.value;callback.call(opt_thisArg, +entry[1],entry[0],this)}};PolyfillMap.prototype[Symbol.iterator]=PolyfillMap.prototype.entries;var maybeGetEntry=function(map,key){var id=getId(key);var list=map.data_[id];if(list&&$jscomp.owns(map.data_,id))for(var index=0;index-.25){var y=x;var d=1;var z=x;var zPrev=0;var s=1;while(zPrev!=z){y*=x;s*=-1;z=(zPrev=z)+s*y/++d}return z}return Math.log(1+x)};return polyfill},"es6","es3");$jscomp.polyfill("Math.atanh",function(orig){if(orig)return orig;var log1p=Math.log1p;var polyfill=function(x){x=Number(x);return(log1p(x)-log1p(-x))/2};return polyfill},"es6","es3"); +$jscomp.polyfill("Math.cbrt",function(orig){if(orig)return orig;var polyfill=function(x){if(x===0)return x;x=Number(x);var y=Math.pow(Math.abs(x),1/3);return x<0?-y:y};return polyfill},"es6","es3"); +$jscomp.polyfill("Math.clz32",function(orig){if(orig)return orig;var polyfill=function(x){x=Number(x)>>>0;if(x===0)return 32;var result=0;if((x&4294901760)===0){x<<=16;result+=16}if((x&4278190080)===0){x<<=8;result+=8}if((x&4026531840)===0){x<<=4;result+=4}if((x&3221225472)===0){x<<=2;result+=2}if((x&2147483648)===0)result++;return result};return polyfill},"es6","es3"); +$jscomp.polyfill("Math.cosh",function(orig){if(orig)return orig;var exp=Math.exp;var polyfill=function(x){x=Number(x);return(exp(x)+exp(-x))/2};return polyfill},"es6","es3");$jscomp.polyfill("Math.expm1",function(orig){if(orig)return orig;var polyfill=function(x){x=Number(x);if(x<.25&&x>-.25){var y=x;var d=1;var z=x;var zPrev=0;while(zPrev!=z){y*=x/++d;z=(zPrev=z)+y}return z}return Math.exp(x)-1};return polyfill},"es6","es3"); +$jscomp.polyfill("Math.fround",function(orig){if(orig)return orig;if($jscomp.SIMPLE_FROUND_POLYFILL||typeof Float32Array!=="function")return function(arg){return arg};var arr=new Float32Array(1);var polyfill=function(arg){arr[0]=arg;return arr[0]};return polyfill},"es6","es3"); +$jscomp.polyfill("Math.hypot",function(orig){if(orig)return orig;var polyfill=function(var_args){if(arguments.length<2)return arguments.length?Math.abs(arguments[0]):0;var i,z,sum,max;for(max=0,i=0;i1E100||max<1E-100){if(!max)return max;sum=0;for(i=0;i>>16&65535;var al=a&65535;var bh=b>>>16&65535;var bl=b&65535;var lh=ah*bl+al*bh<<16>>>0;return al*bl+lh|0};return polyfill},"es6","es3");$jscomp.polyfill("Math.log10",function(orig){if(orig)return orig;var polyfill=function(x){return Math.log(x)/Math.LN10};return polyfill},"es6","es3"); +$jscomp.polyfill("Math.log2",function(orig){if(orig)return orig;var polyfill=function(x){return Math.log(x)/Math.LN2};return polyfill},"es6","es3");$jscomp.polyfill("Math.sign",function(orig){if(orig)return orig;var polyfill=function(x){x=Number(x);return x===0||isNaN(x)?x:x>0?1:-1};return polyfill},"es6","es3");$jscomp.polyfill("Math.sinh",function(orig){if(orig)return orig;var exp=Math.exp;var polyfill=function(x){x=Number(x);if(x===0)return x;return(exp(x)-exp(-x))/2};return polyfill},"es6","es3"); +$jscomp.polyfill("Math.tanh",function(orig){if(orig)return orig;var polyfill=function(x){x=Number(x);if(x===0)return x;var y=Math.exp(-2*Math.abs(x));var z=(1-y)/(1+y);return x<0?-z:z};return polyfill},"es6","es3");$jscomp.polyfill("Math.trunc",function(orig){if(orig)return orig;var polyfill=function(x){x=Number(x);if(isNaN(x)||x===Infinity||x===-Infinity||x===0)return x;var y=Math.floor(Math.abs(x));return x<0?-y:y};return polyfill},"es6","es3"); +$jscomp.polyfill("Number.EPSILON",function(orig){return Math.pow(2,-52)},"es6","es3");$jscomp.polyfill("Number.MAX_SAFE_INTEGER",function(){return 9007199254740991},"es6","es3");$jscomp.polyfill("Number.MIN_SAFE_INTEGER",function(){return-9007199254740991},"es6","es3");$jscomp.polyfill("Number.isFinite",function(orig){if(orig)return orig;var polyfill=function(x){if(typeof x!=="number")return false;return!isNaN(x)&&x!==Infinity&&x!==-Infinity};return polyfill},"es6","es3"); +$jscomp.polyfill("Number.isInteger",function(orig){if(orig)return orig;var polyfill=function(x){if(!Number.isFinite(x))return false;return x===Math.floor(x)};return polyfill},"es6","es3");$jscomp.polyfill("Number.isNaN",function(orig){if(orig)return orig;var polyfill=function(x){return typeof x==="number"&&isNaN(x)};return polyfill},"es6","es3"); +$jscomp.polyfill("Number.isSafeInteger",function(orig){if(orig)return orig;var polyfill=function(x){return Number.isInteger(x)&&Math.abs(x)<=Number.MAX_SAFE_INTEGER};return polyfill},"es6","es3");$jscomp.polyfill("Number.parseFloat",function(orig){return orig||parseFloat},"es6","es3");$jscomp.polyfill("Number.parseInt",function(orig){return orig||parseInt},"es6","es3"); +$jscomp.assign=typeof Object.assign=="function"?Object.assign:function(target,var_args){for(var i=1;i3?opt_receiver:target,value);return true}else if(property.writable&&!Object.isFrozen(target)){target[propertyKey]=value;return true}return false};return polyfill},"es6", +"es5");$jscomp.polyfill("Reflect.setPrototypeOf",function(orig){if(orig)return orig;else if($jscomp.setPrototypeOf){var setPrototypeOf=$jscomp.setPrototypeOf;var polyfill=function(target,proto){try{setPrototypeOf(target,proto);return true}catch(e){return false}};return polyfill}else return null},"es6","es5"); +$jscomp.polyfill("Set",function(NativeSet){function isConformant(){if($jscomp.ASSUME_NO_NATIVE_SET||!NativeSet||typeof NativeSet!="function"||!NativeSet.prototype.entries||typeof Object.seal!="function")return false;try{NativeSet=NativeSet;var value=Object.seal({x:4});var set=new NativeSet($jscomp.makeIterator([value]));if(!set.has(value)||set.size!=1||set.add(value)!=set||set.size!=1||set.add({x:4})!=set||set.size!=2)return false;var iter=set.entries();var item=iter.next();if(item.done||item.value[0]!= +value||item.value[1]!=value)return false;item=iter.next();if(item.done||item.value[0]==value||item.value[0].x!=4||item.value[1]!=item.value[0])return false;return iter.next().done}catch(err){return false}}if($jscomp.USE_PROXY_FOR_ES6_CONFORMANCE_CHECKS){if(NativeSet&&$jscomp.ES6_CONFORMANCE)return NativeSet}else if(isConformant())return NativeSet;$jscomp.initSymbolIterator();var PolyfillSet=function(opt_iterable){this.map_=new Map;if(opt_iterable){var iter=$jscomp.makeIterator(opt_iterable);var entry; +while(!(entry=iter.next()).done){var item=entry.value;this.add(item)}}this.size=this.map_.size};PolyfillSet.prototype.add=function(value){value=value===0?0:value;this.map_.set(value,value);this.size=this.map_.size;return this};PolyfillSet.prototype["delete"]=function(value){var result=this.map_["delete"](value);this.size=this.map_.size;return result};PolyfillSet.prototype.clear=function(){this.map_.clear();this.size=0};PolyfillSet.prototype.has=function(value){return this.map_.has(value)};PolyfillSet.prototype.entries= +function(){return this.map_.entries()};PolyfillSet.prototype.values=function(){return this.map_.values()};PolyfillSet.prototype.keys=PolyfillSet.prototype.values;PolyfillSet.prototype[Symbol.iterator]=PolyfillSet.prototype.values;PolyfillSet.prototype.forEach=function(callback,opt_thisArg){var set=this;this.map_.forEach(function(value){return callback.call(opt_thisArg,value,value,set)})};return PolyfillSet},"es6","es3"); +$jscomp.checkStringArgs=function(thisArg,arg,func){if(thisArg==null)throw new TypeError("The 'this' value for String.prototype."+func+" must not be null or undefined");if(arg instanceof RegExp)throw new TypeError("First argument to String.prototype."+func+" must not be a regular expression");return thisArg+""}; +$jscomp.polyfill("String.prototype.codePointAt",function(orig){if(orig)return orig;var polyfill=function(position){var string=$jscomp.checkStringArgs(this,null,"codePointAt");var size=string.length;position=Number(position)||0;if(!(position>=0&&position56319||position+1===size)return first;var second=string.charCodeAt(position+1);if(second<56320||second>57343)return first;return(first-55296)*1024+ +second+9216};return polyfill},"es6","es3");$jscomp.polyfill("String.prototype.endsWith",function(orig){if(orig)return orig;var polyfill=function(searchString,opt_position){var string=$jscomp.checkStringArgs(this,searchString,"endsWith");searchString=searchString+"";if(opt_position===void 0)opt_position=string.length;var i=Math.max(0,Math.min(opt_position|0,string.length));var j=searchString.length;while(j>0&&i>0)if(string[--i]!=searchString[--j])return false;return j<=0};return polyfill},"es6","es3"); +$jscomp.polyfill("String.fromCodePoint",function(orig){if(orig)return orig;var polyfill=function(var_args){var result="";for(var i=0;i1114111||code!==Math.floor(code))throw new RangeError("invalid_code_point "+code);if(code<=65535)result+=String.fromCharCode(code);else{code-=65536;result+=String.fromCharCode(code>>>10&1023|55296);result+=String.fromCharCode(code&1023|56320)}}return result};return polyfill},"es6","es3"); +$jscomp.polyfill("String.prototype.includes",function(orig){if(orig)return orig;var polyfill=function(searchString,opt_position){var string=$jscomp.checkStringArgs(this,searchString,"includes");return string.indexOf(searchString,opt_position||0)!==-1};return polyfill},"es6","es3"); +$jscomp.polyfill("String.prototype.repeat",function(orig){if(orig)return orig;var polyfill=function(copies){var string=$jscomp.checkStringArgs(this,null,"repeat");if(copies<0||copies>1342177279)throw new RangeError("Invalid count value");copies=copies|0;var result="";while(copies){if(copies&1)result+=string;if(copies>>>=1)string+=string}return result};return polyfill},"es6","es3"); +$jscomp.stringPadding=function(padString,padLength){var padding=padString!==undefined?String(padString):" ";if(!(padLength>0)||!padding)return"";var repeats=Math.ceil(padLength/padding.length);return padding.repeat(repeats).substring(0,padLength)}; +$jscomp.polyfill("String.prototype.padEnd",function(orig){if(orig)return orig;var padEnd=function(targetLength,opt_padString){var string=$jscomp.checkStringArgs(this,null,"padStart");var padLength=targetLength-string.length;return string+$jscomp.stringPadding(opt_padString,padLength)};return padEnd},"es8","es3"); +$jscomp.polyfill("String.prototype.padStart",function(orig){if(orig)return orig;var padStart=function(targetLength,opt_padString){var string=$jscomp.checkStringArgs(this,null,"padStart");var padLength=targetLength-string.length;return $jscomp.stringPadding(opt_padString,padLength)+string};return padStart},"es8","es3"); +$jscomp.polyfill("String.prototype.startsWith",function(orig){if(orig)return orig;var polyfill=function(searchString,opt_position){var string=$jscomp.checkStringArgs(this,searchString,"startsWith");searchString=searchString+"";var strLen=string.length;var searchLen=searchString.length;var i=Math.max(0,Math.min(opt_position|0,string.length));var j=0;while(j=searchLen};return polyfill},"es6","es3"); +$jscomp.arrayFromIterator=function(iterator){var i;var arr=[];while(!(i=iterator.next()).done)arr.push(i.value);return arr};$jscomp.arrayFromIterable=function(iterable){if(iterable instanceof Array)return iterable;else return $jscomp.arrayFromIterator($jscomp.makeIterator(iterable))}; +$jscomp.inherits=function(childCtor,parentCtor){childCtor.prototype=$jscomp.objectCreate(parentCtor.prototype);childCtor.prototype.constructor=childCtor;if($jscomp.setPrototypeOf){var setPrototypeOf=$jscomp.setPrototypeOf;setPrototypeOf(childCtor,parentCtor)}else for(var p in parentCtor){if(p=="prototype")continue;if(Object.defineProperties){var descriptor=Object.getOwnPropertyDescriptor(parentCtor,p);if(descriptor)Object.defineProperty(childCtor,p,descriptor)}else childCtor[p]=parentCtor[p]}childCtor.superClass_= +parentCtor.prototype}; +$jscomp.polyfill("WeakSet",function(NativeWeakSet){function isConformant(){if(!NativeWeakSet||!Object.seal)return false;try{var x=Object.seal({});var y=Object.seal({});var set=new NativeWeakSet([x]);if(!set.has(x)||set.has(y))return false;set["delete"](x);set.add(y);return!set.has(x)&&set.has(y)}catch(err){return false}}if($jscomp.USE_PROXY_FOR_ES6_CONFORMANCE_CHECKS){if(NativeWeakSet&&$jscomp.ES6_CONFORMANCE)return NativeWeakSet}else if(isConformant())return NativeWeakSet;var PolyfillWeakSet=function(opt_iterable){this.map_= +new WeakMap;if(opt_iterable){var iter=$jscomp.makeIterator(opt_iterable);var entry;while(!(entry=iter.next()).done){var item=entry.value;this.add(item)}}};PolyfillWeakSet.prototype.add=function(elem){this.map_.set(elem,true);return this};PolyfillWeakSet.prototype.has=function(elem){return this.map_.has(elem)};PolyfillWeakSet.prototype["delete"]=function(elem){return this.map_["delete"](elem)};return PolyfillWeakSet},"es6","es3"); +(function(){var Module=function(id,opt_exports){this.id=id;this.exports=opt_exports||{}};Module.prototype.exportAllFrom=function(other){var module=this;var define={};for(var key in other){if(key=="default"||key in module.exports||key in define)continue;define[key]={enumerable:true,get:function(key){return function(){return other[key]}}(key)}}$jscomp.global.Object.defineProperties(module.exports,define)};var CacheEntry=function(def,module,path){this.def=def;this.module=module;this.path=path;this.blockingDeps= +new Set};CacheEntry.prototype.load=function(){if(this.def){var def=this.def;this.def=null;callRequireCallback(def,this.module)}return this.module.exports};function callRequireCallback(callback,opt_module){var oldPath=currentModulePath;try{if(opt_module){currentModulePath=opt_module.id;callback.call(opt_module,createRequire(opt_module),opt_module.exports,opt_module)}else callback($jscomp.require)}finally{currentModulePath=oldPath}}var moduleCache=new Map;var currentModulePath="";function normalizePath(path){var components= +path.split("/");var i=0;while(i0}function qZ(a,b){return iZ(a,b)<0}function Yr(a,b){Qr(a,b);return a}function Vhb(a){vwc(a.c);vwc(a.i)}function Vzc(){throw gZ(new ytc);}function xzc(){throw gZ(new ytc);}function yzc(){throw gZ(new ytc);}function zzc(){throw gZ(new ytc); +}function Czc(){throw gZ(new ytc);}function Dzc(){throw gZ(new ytc);}function Uyc(){throw gZ(new jFc);}function Wbb(){Wbb=JZ;Vbb=new fDc}function yjb(){yjb=JZ;xjb=new Cjb}function a3b(){a3b=JZ;_2b=new b3b}function Kvb(){Kvb=JZ;Jvb=new Ovb}function nEb(){nEb=JZ;mEb=new qEb}function EFb(){EFb=JZ;DFb=new HFb}function _D(){QD!=0&&(QD=0);SD=-1}function qOb(a){a.a=a.i-a.f-a.r-1}function ctb(a){this.a=a;this.b=1}function dyb(){Sd(iRc);this.a=iRc}function d7b(a,b){a.d&&t5b(a.a,b)}function c7b(a,b){a.d&&s5b(a.a, +b)}function hnc(a,b){a!=$mc&&(a.a=b)}function JBc(a,b){return Oqc(a,b)}function jtc(a,b){a.a+=b;return a}function ktc(a,b){a.a+=b;return a}function K$b(a){a.e=true;return a}function JJc(a){lJc(a);return a.a}function Tyc(){Tyc=JZ;Syc=new Vyc}function nFc(){nFc=JZ;mFc=new fDc}function DHc(a){this.a=new yGc(a)}function nf(a,b){this.a=a;this.b=b}function Oj(a,b){this.a=a;this.b=b}function Aj(a,b){this.a=a;this.c=b}function Fj(a,b){this.a=a;this.c=b}function Wh(a,b){this.b=a;this.c=b}function Wg(a,b){this.b= +a;this.a=b}function El(a,b){this.b=a;this.a=b}function kl(a,b){this.a=a;this.b=b}function ql(a,b){this.a=a;this.b=b}function sl(a,b){this.a=a;this.b=b}function Cl(a,b){this.a=a;this.b=b}function Ml(a,b){this.a=a;this.b=b}function Ol(a,b){this.a=a;this.b=b}function pm(a,b){this.a=a;this.b=b}function fq(a,b){this.a=a;this.b=b}function Sq(a,b){this.b=a;this.a=b}function nr(a,b){this.b=a;this.a=b}function he(a,b){this.e=a;this.f=b}function Wq(a,b){this.g=a;this.i=b}function Ws(a,b){this.a=a;this.b=b} +function gw(a,b){this.a=a;this.b=b}function vw(a,b){this.a=a;this.f=b}function Dz(a,b){this.a=a;this.b=b}function uA(a,b){this.a=a;this.b=b}function _B(a,b){this.b=a;this.a=b}function te(a,b){he.call(this,a,b)}function PB(a,b){IB.call(this,a,b)}function HD(a,b){PB.call(this,a,b)}function Mv(a,b){return Utc(a.b,b)}function Oo(a,b){return rAc(a.d,b)}function h$(a,b){return pEc(a.e,b)}function j$(a,b){return oEc(a.e,b)}function GC(a){return zsc(a.a,a.b)}function Yb(a){return a>=65&&a<=90}function Hr(a, +b){return a>b&&b<$Lc}function iB(a,b){return a.g[b%a.d]}function lZ(a,b){return iZ(a,b)==0}function tZ(a,b){return iZ(a,b)!=0}function H$(a,b){he.call(this,a,b)}function F2(a,b){he.call(this,a,b)}function b6(a,b){he.call(this,a,b)}function c9(a,b){he.call(this,a,b)}function D9(a,b){he.call(this,a,b)}function I9(a,b){he.call(this,a,b)}function P9(a,b){he.call(this,a,b)}function V9(a,b){he.call(this,a,b)}function _9(a,b){he.call(this,a,b)}function F5(a,b){m5.call(this,a,b)}function n8(a,b){this.a=a; +this.b=b}function U7(a,b){this.d=a;this.c=b}function HFc(a,b){while(a.Aj(b));}function w7(a,b){s7(a,G6(b));i7(a)}function Ybb(a,b){return a.b.Sd(b)}function lQb(a,b){return a.a.Sd(b)}function cnc(a,b){return a.b.Ie(b)}function Azc(a,b){return a.b.Sd(b)}function Bzc(a,b){return a.b.Td(b)}function Ezc(a,b){return a.b.ae(b)}function pAc(a,b){return a.d.Ge(b)}function rAc(a,b){return zb(a.d,b)}function R2b(a,b){return Po(a.a,b)}function NAc(a,b){return a.b.Sd(b)}function kDc(a,b){return a.a.se(b)}function duc(a){return a.d.c+ +a.e.b}function aDc(a){this.c=a;ZCc(this)}function xGc(){yGc.call(this,null)}function rJc(){nJc.call(this,null)}function qKc(a,b,c){a.splice(b,c)}function sKb(a,b,c){t9b(a,c.c-b.c)}function Urb(a,b,c){fpb(a.b,a,b,c)}function i2b(a,b,c){ntc(a.c,b,0,c)}function Ue(a,b){return a.c.je(a,b)}function $C(a,b){return b.b+a.c.i.b}function i9(a,b){return lbb(a.wd,b)}function j9(a,b){return mbb(a.wd,b)}function p9(a,b){a.hc=new Ie(Sd(b))}function eab(a,b){he.call(this,a,b)}function xab(a,b){he.call(this,a,b)} +function Eab(a,b){he.call(this,a,b)}function Kab(a,b){he.call(this,a,b)}function Qab(a,b){he.call(this,a,b)}function Yab(a,b){he.call(this,a,b)}function cbb(a,b){he.call(this,a,b)}function Tbb(a,b){he.call(this,a,b)}function kz(a){oo();Ro.call(this,a)}function nz(a){mz();wp.call(this,a)}function Xcb(a,b){he.call(this,a,b)}function gcb(a,b){this.a=a;this.b=b}function geb(a,b){this.a=a;this.b=b}function Peb(a,b){this.a=a;this.b=b}function dfb(a,b){this.f=a;this.b=b}function Lfb(a,b){this.a=a;this.c= +b}function kgb(a,b){this.b=a;this.a=b}function qgb(a,b){he.call(this,a,b)}function Xgb(a,b){he.call(this,a,b)}function Zgb(a,b){this.b=a;this.a=b}function Ahb(a,b){this.b=a;this.a=b}function rib(a,b){this.a=a;this.b=b}function Jib(a){this.a=a;this.b=ANc}function Mib(a,b){this.b=a;this.a=b}function Slb(a,b){he.call(this,a,b)}function dmb(a,b){he.call(this,a,b)}function rnb(a,b){HD.call(this,a,b)}function Fr(){tj.call(this,new fDc)}function Cm(){this.c=(Gb(),Gb(),Fb)}function Qrb(a,b){this.b=a;this.a= +b}function Vrb(a,b){this.b=a;this.a=b}function $rb(a,b){this.b=a;this.a=b}function Dsb(a,b){this.j=a;this.i=b}function utb(a,b){this.b=a;this.a=b}function jtb(a,b){he.call(this,a,b)}function mub(a,b){he.call(this,a,b)}function Gub(a,b){this.a=a;this.b=b}function Gvb(a,b){this.a=a;this.b=b}function bvb(a,b){this.a=a;this.b=b}function xwb(a,b){this.a=a;this.b=b}function dxb(a,b){this.b=a;this.a=b}function jxb(a,b){he.call(this,a,b)}function Mwb(a,b){he.call(this,a,b)}function txb(a,b){he.call(this, +a,b)}function mxb(){jxb.call(this,COc,0)}function vxb(){txb.call(this,gRc,0)}function fDb(a,b){he.call(this,a,b)}function NEb(a,b){he.call(this,a,b)}function xFb(a,b){he.call(this,a,b)}function OFb(a,b){he.call(this,a,b)}function mGb(a,b){PB.call(this,a,b)}function UIb(a,b){he.call(this,a,b)}function nJb(a,b){he.call(this,a,b)}function GJb(a,b){he.call(this,a,b)}function LJb(a,b){he.call(this,a,b)}function qFb(a,b){this.b=a;this.a=b}function FNb(a,b){this.a=a;this.b=b}function KNb(a,b){he.call(this, +a,b)}function gOb(a,b){he.call(this,a,b)}function FOb(a,b){this.c=a;this.b=b}function IOb(a,b){this.a=a;this.b=b}function keb(a,b){return Yjb(b,a.b)}function leb(a,b){return Yjb(b,a.f)}function Nnb(a,b){return Yjb(b,a.e)}function xWb(a,b){return b>=0&b=48&&a<=55}function Xb(a){return a>=97&&a<=122}function rXb(a,b){this.a=a;this.b=b}function NRb(a,b){this.e=a;this.d=b}function IZb(a,b){this.p=a;this.o=b}function Z1b(a,b){this.b=a;this.a=b}function Z$b(a,b){he.call(this, +a,b)}function f$b(a,b){he.call(this,a,b)}function f6b(a,b){he.call(this,a,b)}function jWb(a,b){he.call(this,a,b)}function o1b(a,b){he.call(this,a,b)}function H7b(a,b){this.a=a;this.b=b}function nac(a,b){this.c=a;this.b=b}function Ibc(a,b){he.call(this,a,b)}function tcc(a,b){he.call(this,a,b)}function Bfc(a,b){he.call(this,a,b)}function ekc(a,b){he.call(this,a,b)}function ync(a,b){this.b=a;this.a=b}function zoc(a,b){he.call(this,a,b)}function Boc(){zoc.call(this,PSc,0)}function Foc(){zoc.call(this, +OSc,1)}function $D(a){$wnd.clearTimeout(a)}function xic(a){a.e=a.e|2;return a}function Fic(a){a.e=a.e|8;return a}function alc(a){Sd(a.o);return a.o}function Y2b(a,b){X2b();return a[b]}function l6b(a,b){return S4b(a.a,b)}function Qtc(a,b){return Stc(a.a,b)}function uqc(a,b){return zsc(a.a,b)}function xqc(a,b){return Usc(a.a,b)}function gzc(a,b){return Utc(a.a,b)}function Jtc(a,b){he.call(this,a,b)}function Jrc(a,b){HD.call(this,a,b)}function Iqc(a,b){IB.call(this,a,b)}function sBc(a,b){this.a=a;this.b= +b}function wvc(a,b){this.d=a;this.e=b}function GCc(a,b){this.b=a;this.a=b}function pHc(a,b){he.call(this,a,b)}function BFc(a){tFc.call(this,a,21)}function VJc(a,b){this.a=a;this.b=b}function $Jc(a,b){this.a=a;this.b=b}function jKc(a,b){this.b=a;this.a=b}function Tic(a,b){Xd(!a.$i());a.p=b}function oEc(a,b){return Utc(a.c,b)}function YDc(a,b){return a.a.get(b)}function Duc(a){return a.b=48&&a<=57}function qF(a){return typeof a=== +XKc}function rF(a){return typeof a===YKc}function tF(a){return typeof a===$Kc}function uF(a){return a==null?null:a}function Hg(a){return!a?null:a.We()}function FC(a){return zsc(a.a,a.b++)}function Xn(a,b){return zb(a.xf(),b)}function Yn(a,b){return a.xf().ce(b)}function Zn(a,b){return a.xf().ae(b)}function b_(a){return a_(Se(Ze(),a))}function pZ(a){return typeof a===YKc}function WDc(){SDc();return new RDc}function ci(a){ai(a);return a.d.Yd()}function H8(a){I8.call(this,a,false)}function jsb(a){U7.call(this, +a,true)}function wxb(){txb.call(this,"V3",1)}function B0(a){this.a=a;this.c=false}function Brb(a){this.b=a;this.a=true}function hyb(){this.a=mZ(Date.now())}function SPb(){SPb=JZ;RPb=je(QPb())}function $xb(a,b){a.b=byb(b,a.a,a.b)}function bJb(a,b){a.b=mp(b);return a}function hJb(a,b){a.i=mp(b);return a}function eEb(a,b){a.i=In(b);return a}function cEb(a,b){a.f=In(b);return a}function dEb(a,b){a.g=In(b);return a}function _Db(a,b){a.c=wo(b);return a}function pOb(a,b){a.q[a.r++]=b;--a.b}function tKb(a, +b){vKb(a,b.d.b,b.d.a)}function kQb(a,b){return a.a.Td(b.a)}function ltc(a,b){return a.a+=""+b,a}function Usc(a,b){return a.substr(b)}function O8b(a){return!!a.c&&!a.c.f}function lXb(a){return kXb(a)&&a!=42}function rOb(a){sOb.call(this,a,0,0)}function utc(a){yqc.call(this,NZ(a))}function QFc(a,b){IFc.call(this,a,b)}function oKc(a,b,c){a.splice(b,0,c)}function XJc(a,b,c){b.jf(a.a.Od(c))}function klc(a,b,c){Vf(a.q,c.Xi(),b)}function EWb(a,b,c,d){DOb(a.e,b,c,d)}function ul(a,b,c){return a.d=b.Od(c)} +function wb(a,b){return uF(a)===uF(b)}function Tt(a){return Sd(a),new Gm(a)}function GE(a){return HE(a.l,a.m,a.h)}function Gm(a){this.a=a;Cm.call(this)}function Jm(a){this.a=a;Cm.call(this)}function J1(a){this.a=new yq;this.b=a}function Zf(a){Kd(a.Yd()==0);this.c=a}function etb(a){this.b=a;this.a=a.b.b}function Pv(){this.b=(wx(12),new gDc)}function gob(){iob.call(this,new nob)}function RFb(){RFb=JZ;QFb=new Pc("/")}function ghb(a,b){p8b(a.c,b,F8b(a.c))}function UVb(a,b,c){DOb(a.c,b,"%s",c)}function n7b(a, +b){h4b(a.a,b);a.e=true}function A7(a,b){b>1?a.K=1:a.K=b}function m9b(a,b){a.k=$9b(T9b(a.k),b)}function wWb(a,b){return ZSb(a.i.b,b)}function qTb(a,b){return TSb(b,a.b.d)}function Hsc(a,b){return a.indexOf(b)}function hCc(a,b,c){return gCc(a,b,c)}function Erc(a){return rF(a)?a:a.pj()}function R4b(a){return!!a.d&&!!a.d.n}function N8b(a){return!!a.c&&!!a.c.f}function F8b(a){return a.c?a.c.i:null}function dgc(a){egc.call(this,a,null)}function ZEc(){pDc.call(this,new uEc)}function YEc(){pDc.call(this, +new tEc)}function CKc(){zKc.call(this,"UTF-8")}function ttb(a){utb.call(this,a,false)}function Ti(a,b,c){Pi.call(this,a,b,c)}function Owb(){Mwb.call(this,"LINE",0)}function Yub(){this.a=null;this.b=null}function HDc(a){this.a=WDc();this.b=a}function jA(a,b){this.c=a;this.b=Sd(b)}function Wxc(a,b){Sxc(a,0,a.length,b)}function mtc(a,b){a.a+=""+b;return a}function otc(a,b){a.a+=""+b;return a}function ptc(a,b){a.a+=""+b;return a}function fC(a,b){var c;c=qD(b);eC(a,c)}function l9b(a,b){c9b(a,(Hbc(),Hac), +b)}function n9b(a,b){d9b(a,(Hbc(),Nac),b)}function p9b(a,b){d9b(a,(Hbc(),cbc),b)}function s9b(a,b){d9b(a,(Hbc(),Dbc),b)}function y9b(a,b){d9b(a,(Hbc(),ybc),b)}function Hyb(a,b){Fyb();Iyb(a,b,!b.ad)}function QSb(a){OSb();return Po(pSb,a)}function jcc(a){n8b();J9b.call(this,a)}function Wgc(a){Ofc();dgc.call(this,a)}function thc(a){Ofc();qhc.call(this,a)}function vmc(a){Ofc();imc.call(this,a)}function Fmc(a){Ofc();vmc.call(this,a)}function Mmc(a){Ofc();qhc.call(this,a)}function Smc(a){Ofc();qhc.call(this, +a)}function Anc(a){Ofc();qhc.call(this,a)}function Enc(a){Ofc();qhc.call(this,a)}function mqc(a){Ofc();qhc.call(this,a)}function $t(a){return wu(a.a.Vd(),a.b)}function w4b(a){return!a.d?null:a.d.b}function x4b(a){return!a.d?null:a.d.c}function y4b(a){return!a.d?null:a.d.d}function D4b(a){return!a.d?null:a.d.j}function E4b(a){return!a.d?null:a.d.k}function lFc(a){return a!=null?Db(a):0}function lsc(a,b){return iZ(a,b)>0?a:b}function Esc(a,b){return uF(a)===uF(b)}function wlc(a,b){return b?b:a.e.u[51]} +function bgc(a){return pF(a,48)?a:null}function cgc(a){return pF(a,48)?a:null}function _Bc(a){return a<10?"0"+a:""+a}function qBc(a,b,c){return JBc(b.a,c.a)}function MIc(a,b){if(zIc)return;a.b=b}function Js(a,b){Bn();this.a=a;this.b=b}function at(a,b){Bn();Ws.call(this,a,b)}function Ji(a){this.a=a;Di.call(this,a)}function rr(a){this.a=a;Hh.call(this,a)}function wr(a){this.a=a;Hh.call(this,a)}function HC(a){this.a=a;this.c=a.length}function yHc(){pHc.call(this,"Tail",3)}function tHc(){pHc.call(this, +"Head",1)}function Ce(){te.call(this,"IS_NULL",2)}function z3(){y3();A3.call(this,new e6)}function Kdb(a){this.a=a;this.b=new fxc}function deb(a){this.b=a;this.a=new Cwc}function Erb(a){this.b=a;this.a=new mDc}function Iub(a){this.b=a;this.a=new tEc}function Ofb(a){this.b=new YEc;this.a=a}function $ub(a){this.c=new YEc;this.a=a}function XCb(a){this.b=a;this.a=new fxc}function bxb(){this.a=(scc(),qcc);Ptc()}function O3b(a){return n8b(),new tac(a)}function pfb(a,b){return _kc(U6(a.a),b)}function pTb(a, +b){return new Z1b(b,a.f)}function jQb(a,b){return a.a.Td(LCc(b))}function Lrc(a,b){return ab?1:0}function HE(a,b,c){return{l:a,m:b,h:c}}function Q9b(a,b){n8b();return(a&b)==b}function Icb(a,b){_tc(Gcb,a,b);return b}function Ps(a,b){Sd(b);Pr(a,b);return a}function dxc(a,b){Vxc(a.a,a.a.length,b)}function $Cc(a){return a.a=a);this.b=a;this.a=b}function tj(a){Kd(a.d.c+a.e.b==0);this.a=a}function Fuc(a){a.d.fe(a.c);a.b=a.c;a.c=-1}function cuc(a){a.d=new HDc(a);a.e=new _Dc}function Ttc(a){a.d=new HDc(a);a.e=new _Dc}function mEc(a){a.b=new GEc(a);a.c=new fDc}function _7(){this.c=new stc;this.d=new mDc}function Lm(a,b){this.a=b;Af.call(this,a,0)}function acb(a){Wbb();$bb.call(this,null,a)}function bcb(a){Wbb();_bb.call(this,null,a)}function IBc(a){HBc();return a==EBc?null:a}function Ieb(a){Feb();this.a=a;this.b= +false}function R3(a,b){a.Mg();b&&a.Ug();a.s=false}function P3(a){Xd(a.u==a.v);a.Lg("`");++a.u}function E8(a,b){Kd(!b||!a.k||a.k==b);a.k=b}function $l(a,b){var c;c=a.a;a.a=b;return c}function T6(a){!a.N&&(a.N=h7(a));return a.N}function z8b(a){Sd(a.g);e9b(a.g,a);return a}function Tgb(a){this.b=(Wgb(),Ugb);this.a=a}function lOb(a){return a.g==a.d?a.c+a.a:a.a}function Bwc(a){return a.c-a.b&a.a.length-1}function Csc(a){return ftc(BKc(a,a.length))}function t9(a){return a.xc||a.zb==($9(),X9)}function PCc(a, +b){return pF(b,12)&&QCc(a,b)}function RCc(a,b){return pF(b,12)&&SCc(a,b)}function _wb(a,b,c){return new cxb(b,c,a.a)}function FIc(a,b){if(zIc)return;Ywc(a.a,b)}function drc(a){if(a.p!=null)return;urc(a)}function XBc(a){this.a=new $wnd.Date(xZ(a))}function Qgc(a){Ofc();return!a?null:a.Ii()}function S9b(a){n8b();return a==-1?-1:a&xMc}function Bc(a,b){return a.a.Md(b)||a.b.Md(b)}function fg(a){return pF(a,54)?a.de():a.Vd()}function nF(a){return String.fromCharCode(a)}function iFc(){GD.call(this,"no key found")} +function $cb(){Xcb.call(this,"MULTILINE",1)}function ze(){te.call(this,"ALWAYS_FALSE",1)}function Bm(){pm.call(this,(oo(),oo(),no),0)}function I8(a,b){G8.call(this,new wnb(a),b)}function KJc(a,b){nJc.call(this,a);this.a=b}function Mbb(a,b){this.a=a;this.c=b;this.b=0}function Tu(a){this.a=(Nu(),Mu);this.d=Sd(a)}function inc(){_mc();jnc.call(this,new xGc)}function RKc(){RKc=JZ;OKc=new xb;QKc=new xb}function Feb(){Feb=JZ;Eeb=Qe(Ye(new nc(46)))}function dKb(a,b){return w8b(new J9b(b),a.s)}function qQb(a, +b){return new uQb(AQb(a.a,b))}function UD(a,b,c){return a.apply(b,c);var d}function r7(a,b,c){c.b=a;return PHc(a.u,b,c)}function V_(a,b){b0((Afc(),pec),b);r0(a,b.c)}function K6(a){!a.G&&_6(a,new v9);return a.g}function _h(a){a.b?_h(a.b):a.f.c.Je(a.e,a.d)}function n_b(a){IZb.call(this,(n1b(),B0b),a)}function r_b(a){IZb.call(this,(n1b(),G0b),a)}function s_b(a){IZb.call(this,(n1b(),H0b),a)}function w$b(a){IZb.call(this,(n1b(),b0b),a)}function h$b(a){IZb.call(this,(n1b(),P_b),a)}function i$b(a){IZb.call(this, +(n1b(),Q_b),a)}function j$b(a){IZb.call(this,(n1b(),O_b),a)}function r$b(a){IZb.call(this,(n1b(),Y_b),a)}function x1b(a){IZb.call(this,(n1b(),U0b),a)}function C1b(a){IZb.call(this,(n1b(),Z0b),a)}function _1b(a){GD.call(this,"codePoint:"+a)}function Zcb(){Xcb.call(this,"SINGLELINE",0)}function adb(){Xcb.call(this,"SOURCELESS",2)}function gkc(a,b,c){hkc.call(this,a,b,c,b.B)}function PDc(a,b){var c;c=a[ISc];c.call(a,b)}function QDc(a,b){var c;c=a[ISc];c.call(a,b)}function pvc(a,b){var c;c=a.e;a.e=b; +return c}function Bvc(a,b){var c;c=b;return!!a.wj(c)}function t6b(a,b){a.e=true;return p4b(a.a,b)}function t5b(a,b){if(!d5b(a))return;a.b.i=b}function lEc(a,b){while(a.Id())b.jf(a.Jd())}function nEc(a){cuc(a.c);a.b.b=a.b;a.b.a=a.b}function xuc(a){a.c.Kd();a.c=null;a.b=vuc(a)}function oJc(a){nJc.call(this,null);this.a=a}function wnb(a){this.a=new _3b(a.b);this.c=a}function uFc(a){this.d=a;this.a=0;this.c=LLc}function xd(a,b,c){this.b=a;this.a=b;this.c=c}function Gl(a,b,c){this.c=b;this.b=c;this.a= +a}function df(a,b,c){this.a=c;bf.call(this,a,b)}function jf(a,b,c){this.a=c;bf.call(this,a,b)}function Qc(a,b,c){Sd(c);return new xd(c,a,b)}function Ye(a){Sd(a);return new We(new $e(a))}function Oc(a){Sd(a);return pF(a,432)?a:NZ(a)}function T9b(a){n8b();return a==-1?-1:a>>>12}function btc(a){return tF(a)?a.length:a.ij()}function nKc(a,b){return sKc(new Array(b),a)}function Wn(a,b){return b!=null&&a.xf().Sd(b)}function zD(a){return new yD(a.d,a.c,a.a,a.b)}function bn(){bn=JZ;new qn((Cyc(),Cyc(),Ayc))} +function wp(a){bn();this.b=(Cyc(),new JAc(a))}function Qo(a){oo();this.d=(Cyc(),new uAc(a))}function Vr(a,b,c){this.a=a;Af.call(this,b,c)}function N1(a,b,c){this.a=a;this.c=b;this.b=c}function a1(a,b,c){this.c=a;this.b=b;this.a=c}function WC(a,b,c){this.j=a;this.b=b;this.a=c}function aB(a,b,c){this.b=a;this.a=b;this.c=c}function c$(a,b,c){he.call(this,a,b);this.a=c}function r$(a,b,c){he.call(this,a,b);this.a=c}function Dy(a,b){Zf.call(this,a);this.a=Sd(b)}function Hy(a,b){pk.call(this,a);this.a=Sd(b)} +function nvb(a){kvb();this.a=a;this.b=new fDc}function Ddb(a){sdb();this.a=a;this.b=new Cwc}function Zeb(a){Ueb();this.a=a;this.b=new Cwc}function Lwb(){Lwb=JZ;Jwb=new Owb;Kwb=new Pwb}function ixb(){ixb=JZ;gxb=new mxb;hxb=new oxb}function sxb(){sxb=JZ;qxb=new vxb;rxb=new wxb}function wFb(){wFb=JZ;vFb=new zFb;uFb=new BFb}function U6(a){!a.P&&(a.P=new llc);return a.P}function GMb(a){var b;b=a.o;a.o=null;return b}function Hnb(a,b){if(a.a)return a.a;return b}function cib(a,b){oib(a.j,b);Ywc(a.a,b);a.j= +b}function RC(a,b){SC(a,"file",false);a.a+=""+b}function e3b(a,b){return i3b((Afc(),vcc),a,b)}function L3b(a,b){return i3b((Afc(),vec),a,b)}function G3b(a,b){return i3b((Afc(),lec),a,b)}function o3b(a,b){return i3b((Afc(),edc),a,b)}function s3b(a,b){return i3b((Afc(),xdc),a,b)}function X3b(a,b){return i3b((Afc(),efc),a,b)}function gyb(a){return vZ(mZ(Date.now()),a.a)}function Hfc(a){return new kcc((Afc(),Bcc),a)}function Cjc(a){return Yfc(a.k)?a.A.u[51]:a.k}function Ksc(a,b,c){return a.lastIndexOf(b, +c)}function Vub(a){a.b=U6(a.a);return new Rub(a)}function _Vb(a){this.b=a;this.a=(Bn(),new Zr)}function c2b(a){GD.call(this,"Precision: "+a)}function dJc(){this.a=iLc;this.b="";this.c=""}function zJc(a){this.b=a;QFc.call(this,LLc,0)}function Re(a){return new Xe(a.c,true,a.d,a.a)}function Po(a,b){return b==null?null:Bx(a.d,b)}function qs(a,b){return dn(po(a.a)).ce(b).Ve()}function f5b(a,b){!a.d&&(a.d=new L5b);a.d.a=b}function g5b(a,b){!a.d&&(a.d=new L5b);a.d.b=b}function j5b(a,b){!a.d&&(a.d=new L5b); +a.d.d=b}function l5b(a,b){!a.d&&(a.d=new L5b);a.d.i=b}function m5b(a,b){!a.d&&(a.d=new L5b);a.d.j=b}function n5b(a,b){!a.d&&(a.d=new L5b);a.d.k=b}function NIc(a,b){if(zIc)return;!!b&&(a.d=b)}function Lcb(a,b){return pF(b,5)&&Esc(b.b,a.b)}function Qmb(a,b){Kd(true);b.k=null;bxc(a.d,b)}function _sb(a,b){this.c=a;this.b=b.d;this.a=b}function Mcb(a,b,c){this.b=a;this.c=b;this.a=c}function smb(a,b,c){this.a=a;this.c=b;this.b=c}function hub(a,b,c){he.call(this,a,b);this.a=c}function Ivb(a,b,c){this.b=a; +this.c=b;this.a=c}function du(a){this.a=a;this.b=1;Cm.call(this)}function iAb(a,b,c){this.a=a;this.b=b;this.c=c}function pDb(a,b,c){he.call(this,a,b);this.a=c}function AJb(a,b,c){he.call(this,a,b);this.a=c}function ENb(a,b,c){this.c=a;this.b=b;this.a=c}function a5(a,b){b5.call(this,a,b,false,false)}function r0(a,b){while(b){q0(a,b,false);b=b.f}}function Nub(a,b){return Hnb(Gnb(b.d,a.d),a.f)}function Cvb(a,b){return kQb(l9(a.a.G),wab(b))}function mQb(a,b){return new uQb(AQb(a.a,b.a))}function pQb(a, +b){return new uQb(AQb(a.a,b.a))}function tQb(a,b){return new uQb(wQb(a.a,b.a))}function PPb(a){NPb();return ne((SPb(),RPb),a)}function reb(){reb=JZ;qeb=tQb((iQb(),$Pb),ZPb)}function Cyc(){Cyc=JZ;Ayc=new Myc;Byc=new bzc}function bFc(){bFc=JZ;_Ec=new cFc;aFc=new eFc}function h5b(a){!a.d&&(a.d=new L5b);K5b(a.d,4)}function o5b(a){!a.d&&(a.d=new L5b);K5b(a.d,5)}function p5b(a){!a.d&&(a.d=new L5b);K5b(a.d,6)}function r5b(a){!a.d&&(a.d=new L5b);K5b(a.d,0)}function u5b(a){!a.d&&(a.d=new L5b);K5b(a.d,3)}function v5b(a){!a.d&& +(a.d=new L5b);K5b(a.d,2)}function z5b(a){!a.d&&(a.d=new L5b);K5b(a.d,1)}function eoc(a,b,c){return b==c||b==Unc(a.c,c)}function I4b(a){return(HPc&a.a)==$Lc?a.i:null}function M4b(a){return(HPc&a.a)==WPc?a.i:null}function uwc(a){return a.a[a.c-1&a.a.length-1]}function Hhc(a,b){egc.call(this,a,b);xhc(this)}function Ikc(a,b,c){he.call(this,a,b);this.a=c}function DZb(a,b,c){he.call(this,a,b);this.a=c}function $Zb(a,b,c){this.c=a;this.a=b;this.b=c}function TCc(a,b,c){this.a=a;this.b=b;this.c=c}function hEc(a, +b,c){this.a=a;this.b=b;this.c=c}function Di(a){this.d=a;Ai(this);this.b=fg(a.d)}function hxc(a){Wwc(this);pKc(this.a,0,a._d())}function rBc(a,b,c){return Lrc(a.Oh(b),a.Oh(c))}function uZ(a,b){return jZ($E(pZ(a)?wZ(a):a,b))}function wj(a,b){if(!a)return 0;return $l(a,b)}function jl(a,b){Sd(a);Sd(b);return new kl(a,b)}function Wt(a,b){Sd(a);Sd(b);return new _t(a,b)}function Yjb(a,b){Rjb();!!b&&(a.d=b,a);return a}function Hic(a,b){a.k=Xkc(a.f,b,null);return a}function mrc(){var a;a=jrc(null);a.i=2;return a} +function Nw(a){var b;b=new fxc;mu(b,a);return b}function sz(a){var b;b=new mDc;mu(b,a);return b}function H6(a){var b;b=a.G.F;b=b?b:a.e;return b}function q9b(a,b){d9b(a,(Hbc(),mbc),b);return a}function gib(a,b,c){nib(a.j,b,Ghb(a,b,false,c))}function dTb(a,b,c){NRb.call(this,a,c);this.c=b}function oac(a,b,c){nac.call(this,a,c);this.a=b}function yac(a,b,c){nac.call(this,a,c);this.a=b}function HEc(a,b,c){this.c=a;wvc.call(this,b,c)}function pKc(a,b,c){mKc(c,0,a,b,c.length,false)}function Ywc(a,b){a.a[a.a.length]= +b;return true}function Rsb(a,b){a.r=b;a.a=C1(new J1(a.b),a.d)}function zo(a,b){oo();el(a,b);return new Wq(a,b)}function Ho(a,b){oo();return el(a,b),new Sz(a,b)}function uCb(a){qCb();this.b=a;this.a=new B0(a)}function Gs(a){this.b=a;this.a=po(this.b.a).uf()}function _t(a,b){this.a=a;this.b=b;Cm.call(this)}function tzb(){jsb.call(this,"es6ConvertSuper")}function Izb(){jsb.call(this,"lateConvertEs6")}function Xyb(){jsb.call(this,"Es6RewriteClass")}function zc(){vc.call(this,"CharMatcher.none()")}function yZ(a){if(pZ(a))return a| +0;return bF(a)}function krc(a){var b;b=jrc(a);yrc(a,b);return b}function $Gc(a,b){return!!b&&_Gc(a,b.d)?b:null}function yfb(a){return a.n==(Afc(),Jdc)||Aqb(a)}function E0(a){return a.b==(Sbb(),Qbb)||a.b==Pbb}function Flb(a){return!a.n?a.b:Flb(a.n)+"."+a.b}function $Jb(a,b){return!!a.b&&a.b.a.a.c<=b.b.c}function nkb(a,b){return Ssc(b,a.g+"_const"+_Pc)}function eKb(a,b,c){return w8b(new L9b(b,c),a.s)}function rQb(a,b){return new uQb(AQb(a.a,op(b)))}function jB(a,b){return b0?1:0}function ylc(a,b){a.e=b;pF(b,48)?a.d=b:a.d=null}function Nxc(a,b){var c;for(c=0;c>1;return b?-a:a}function yGc(a){this.b=null;this.a=(HBc(),!a?EBc:a)}function IFc(a,b){this.e=a;this.d=(b&64)!=0?b|JLc:b}function Vi(a,b,c){this.a=a;di.call(this,a,b,c,null)}function Xs(a,b){Bn();Ws.call(this,a,Vn(new eyc(b)))}function wpb(a,b,c){ppb();return Zpb(a,new nrb(b),c)}function rmb(a,b){return b==a.c?a:new smb(a.a,b,a.b)}function Im(a){return new Tu(new Lm(a.a.length,a.a))}function aq(a, +b){return new aB(a.Ve(),b.Ve(),b.We())}function hKb(a,b){return w8b((n8b(),new tac(b)),a.s)}function fKb(a,b,c,d){return w8b(new N9b(b,c,d),a.s)}function Amb(a,b,c){return c?"{"+Dmb(a,c.a)+"} "+b:b}function CDb(a,b,c){wDb();this.b=a;this.a=b;this.c=c}function J9b(a){n8b();this.n=a;this.g=null;this.k=-1}function Wbc(a,b){n8b();J9b.call(this,a);Ubc(this,b)}function Kyb(a,b){Fyb();a.g.a.d!=0||z7(a,tQb(a.p,b))}function K5b(a,b){var c;c=(Ld(b>=0,lTc),1<0);Xd(a.u==a.v);a.Lg("}");--a.v}function A8(a){if(a.g)return In(a.o);return y8(a).g}function _fc(a){if(pF(a,76))return a.n;return false}function L6(a,b){if(!b)return null;return OHc(a.u,b)}function Dhb(a,b,c,d){return F9b(t3b(Chb(a,b,c,d)),b)}function aib(a,b,c,d){return F9b(S3b(Chb(a,b,c,d)),b)}function sQb(a,b,c){return new uQb(wQb(a.a,MCc(b,c)))}function pFb(a,b){return new qFb(a.b,a.b.c.Mh(a.a,b))}function mhb(a,b){while(b.c)thb(a,g9b(b),null, +null)}function Gsc(a,b,c,d,e){while(ba.length&&(a.length=c)}function uKc(a,b){var c;c=console[a];c.call(console,b)}function axc(a,b){var c;c=a.a[b];qKc(a.a,b,1);return c}function Bhc(a){var b;b=new CHc;anc(a.Vi(),b);return b}function IKc(a){if(a==null)throw gZ(new msc);return a}function xGb(){z1.call(this);this.a=_cb((Wcb(),false))}function Pyb(){jsb.call(this,"Es6ExtractClassExtends")}function wB(){vB.call(this,new kB("base16()",Wsc(fLc)))} +function oo(){oo=JZ;no=new kz(CE(xE(QX,1),PLc,50,0,[]))}function Dd(a){this.b=new Fd;this.c=this.b;this.a=Sd(a)}function Gv(a){this.c=a;this.b=this.c.a;this.a=this.c.e}function it(a,b){ht();Qo.call(this,a);this.a=b;this.c=a}function Ijb(a,b,c){this.a=a;this.c=b;this.d=c;Xd(true)}function E7(a,b){!a.G&&_6(a,new v9);return F7(a,b,true)}function Gd(a,b){return uF(a)===uF(b)||a!=null&&zb(a,b)}function m9(a,b){return kQb(wab(a.Sb),b)&&!kQb(l9(a),b)}function n9(a,b){return lQb(wab(a.Sb),b)&&!lQb(l9(a), +b)}function Phb(a,b){return K3b(b+(a.r.b==0?"":"$"+a.r.b))}function c_(a){return Xd(H3b(a)),new L9b((Afc(),Uec),a)}function q_(a,b){var c;c=C8b(a);b!=c&&D0(QMc+b+RMc+c,a)}function Dmb(a,b){var c;c=new stc;ymb(a,c,b);return c.a}function _Eb(a,b){!b?a.a=new sFb:a.a=b;jEb(a.c,a.a)}function PRb(a,b){NRb.call(this,(AZb(),iYb),a);this.a=b}function KZb(a,b){IZb.call(this,(n1b(),y_b),a);this.a=b}function LZb(a,b){IZb.call(this,(n1b(),z_b),a);this.a=b}function MZb(a,b){IZb.call(this,(n1b(),A_b),a);this.a= +b}function NZb(a,b){IZb.call(this,(n1b(),B_b),a);this.a=b}function OZb(a,b){IZb.call(this,(n1b(),C_b),a);this.a=b}function PZb(a,b){IZb.call(this,(n1b(),D_b),a);this.a=b}function QZb(a,b){IZb.call(this,(n1b(),E_b),a);this.a=b}function SZb(a,b){IZb.call(this,(n1b(),G_b),a);this.a=b}function TZb(a,b){IZb.call(this,(n1b(),H_b),a);this.a=b}function ZZb(a,b){IZb.call(this,(n1b(),N_b),a);this.a=b}function q$b(a,b){IZb.call(this,(n1b(),X_b),a);this.a=b}function s$b(a,b){IZb.call(this,(n1b(),Z_b),a);this.a= +b}function v$b(a,b){IZb.call(this,(n1b(),a0b),a);this.a=b}function A$b(a,b){IZb.call(this,(n1b(),f0b),a);this.a=b}function B$b(a,b){IZb.call(this,(n1b(),g0b),a);this.a=b}function G$b(a,b){IZb.call(this,(n1b(),h0b),a);this.a=b}function a_b(a,b){IZb.call(this,(n1b(),o0b),a);this.a=b}function c_b(a,b){IZb.call(this,(n1b(),q0b),a);this.a=b}function j_b(a,b){IZb.call(this,(n1b(),x0b),a);this.a=b}function p_b(a,b){IZb.call(this,(n1b(),E0b),a);this.a=b}function t_b(a,b){IZb.call(this,(n1b(),I0b),a);this.a= +b}function u_b(a,b){IZb.call(this,(n1b(),J0b),a);this.a=b}function v_b(a,b){IZb.call(this,(n1b(),K0b),a);this.a=b}function x_b(a,b){IZb.call(this,(n1b(),M0b),a);this.a=b}function s1b(a,b){IZb.call(this,(n1b(),P0b),a);this.a=b}function t1b(a,b){IZb.call(this,(n1b(),Q0b),a);this.a=b}function u1b(a,b){IZb.call(this,(n1b(),R0b),a);this.a=b}function w1b(a,b){IZb.call(this,(n1b(),T0b),a);this.a=b}function A1b(a,b){IZb.call(this,(n1b(),X0b),a);this.a=b}function B1b(a,b){IZb.call(this,(n1b(),Y0b),a);this.a= +b}function D1b(a,b){IZb.call(this,(n1b(),$0b),a);this.a=b}function G1b(a,b){IZb.call(this,(n1b(),c1b),a);this.a=b}function H1b(a,b){IZb.call(this,(n1b(),d1b),a);this.a=b}function K1b(a,b){IZb.call(this,(n1b(),f1b),a);this.a=b}function O1b(a,b){IZb.call(this,(n1b(),j1b),a);this.a=b}function Ezb(){jsb.call(this,"es6RewriteRestAndSpread")}function dzb(){jsb.call(this,"Es6RewriteArrowFunction")}function Iqb(a){ppb();return a.n!=(Afc(),pec)&&Jqb(a.g)}function qqb(a){ppb();return a.n==(Afc(),Pdc)||a.n== +Odc}function _Gc(a,b){return nGc(a.c,a.f,b,a.b,a.a,a.e,a.d)}function QHc(a,b,c){return NHc(a,b)?OHc(a,b):PHc(a,b,c)}function fJc(a){if(0>=a)return new rJc;return gJc(a-1)}function lJc(a){if(!a.c){mJc(a);a.d=true}else lJc(a.c)}function Ci(a){ai(a.d);if(a.d.d!=a.c)throw gZ(new WBc);}function T3(a){Xd(a.u>0);Xd(a.u==a.v+1);a.Lg("`");--a.u}function eCc(a){Tc(a.a);a.b=zE(iW,mLc,1,a.b.length,5,1)}function egc(a,b){this.A=a;this.B=!b?Xkc(a,null,null):b}function Xe(a,b,c,d){this.c=a;this.b=b;this.d=c;this.a= +d}function sD(a,b,c,d){this.c=a;this.d=b;this.b=c;this.a=d}function yD(a,b,c,d){this.d=a;this.c=b;this.a=c;this.b=d}function p8(a,b,c,d){this.a=a;this.b=b;this.d=c;this.c=d}function pv(a,b,c,d){Wq.call(this,a,b);this.d=c;this.a=d}function b5(a,b,c,d){he.call(this,a,b);this.b=c;this.a=d}function $q(a,b,c,d){Wq.call(this,a,c);this.a=b;this.f=d}function u5(a,b,c,d){m5.call(this,c,d);this.a=a;this.c=b}function oz(a){wp.call(this,a);this.a=(Cyc(),new kBc(a))}function Rj(a){this.e=a;this.c=new yuc((new puc(a.a)).a)} +function is(a){this.b=(fl(a,"initialArraySize"),new gxc)}function fib(a,b){lib(a.j,b);eib(a,Fhb(a,b));a.j.e=false}function Jyb(a,b,c){Fyb();a.g.a.d!=0||z7(a,sQb(a.p,b,c))}function owb(a,b,c,d,e){u7(a.a,lwb(a,b,c,d,e,(E2(),B2)))}function pwb(a,b,c,d,e){u7(a.a,lwb(a,b,c,d,e,(E2(),D2)))}function gKb(a,b,c,d,e){return w8b(new O9b(b,c,d,e),a.s)}function iKb(a,b,c){return w8b((n8b(),new Wbc(b,c)),a.s)}function kKb(a,b,c){return w8b((n8b(),new ecc(b,c)),a.s)}function c6(){a6();return CE(xE(nL,1),sLc,275, +0,[$5,_5])}function J9(){H9();return CE(xE(EL,1),sLc,262,0,[F9,G9])}function FFb(){EFb();return CE(xE(WQ,1),sLc,264,0,[DFb])}function S3b(a){Xd(H3b(a));return new L9b((Afc(),Uec),a)}function U3b(a){Xd(H3b(a));return new L9b((Afc(),_ec),a)}function Wqc(a){var b;b=a-10;return(b<0?48+a:97+b)&dLc}function zub(a){a.a=new i_;a.c=U6(a.b);return new vub(a)}function Tkc(a,b){var c;c=new Qic(a);Mic(c,b);return c.b}function OMb(a,b){a.o=!a.o?mOb(a.j):GMb(a);return a.o==b}function _pb(a){ppb();Kd(a.n==(Afc(), +ofc));return P8b(a)}function Vrc(a,b){while(b-- >0)a=a<<1|(a<0?1:0);return a}function rwc(a){var b;b=a.a[a.c-1&a.a.length-1];return b}function Rwc(a){this.d=a;this.a=this.d.b;this.b=this.d.c}function sXb(a,b){this.c=a;this.a=b;this.b=new _Sb(this)}function vkc(a,b){this.b=a;this.c=b;this.a=DKc(a)^DKc(b)}function Mkc(a,b,c){return uic(Dic(Bic(new Lic(a),c),b))}function aic(a,b,c,d){return Igc(a,b,c,d)||a.d.Ai(b,c,d)}function $tc(a,b,c){return tF(b)?_tc(a,b,c):FDc(a.d,b,c)}function iCc(a,b){return RCc(a.a, +b)?jCc(a,b.f,null):null}function kFc(a,b){return uF(a)===uF(b)||a!=null&&zb(a,b)}function BZb(a){return a.a==null?(OSb(),Po(pSb,a)).c:a.a}function $sc(a){return String.fromCharCode.apply(null,a)}function qAc(a){!a.b&&(a.b=new QAc(a.d.He()));return a.b}function sAc(a){!a.c&&(a.c=new JAc(a.d.we()));return a.c}function tAc(a){!a.e&&(a.e=new Fzc(a.d.Le()));return a.e}function Yi(a){var b;b=a.c;!b&&(a.c=b=new hk(a));return b}function Zi(a){var b;b=a.d;!b&&(a.d=b=new mk(a));return b}function cu(a){var b; +b=mf(a.a);nu(b,a.b);return new hu(b)}function Tc(a){var b;for(b=a.Vd();b.Id();){b.Jd();b.Kd()}}function Iyc(a){Cyc();return!a?(HBc(),HBc(),GBc):a.nf()}function h_(a){return Yd(H3b(a),a),new L9b((Afc(),Ddc),a)}function Kmb(a,b,c){return new Gmb(null,null,-1,-1,b,a,c)}function vqc(a,b,c,d){a.a=Vsc(a.a,0,b)+(""+d)+Usc(a.a,c)}function kEb(a,b,c,d){this.e=a;this.f=b;this.d=c;this.g=d}function tFc(a,b){this.b=a;this.a=(b&4096)==0?b|64|JLc:b}function dlb(a){this.a=a;this.b=!a.j?new YEc:new $Ec(a.j)}function Zbb(a){Wbb(); +this.a=null;this.b=(bn(),new Zz(a))}function j5(a){this.c=new v9;this.a=new k5(this);this.f=a}function X1b(a,b,c,d){this.d=a;this.c=b;this.b=c;this.a=d}function qWb(a,b,c,d){he.call(this,a,b);this.b=c;this.a=d}function JRb(a,b,c,d){he.call(this,a,b);this.a=c;this.b=d}function tac(a){n8b();J9b.call(this,(Afc(),Cec));this.a=a}function Uic(a,b,c){Xic.call(this,a,b,c,false,null,false)}function Xmc(a,b,c,d){this.c=a;this.e=b;this.b=c;this.d=d}function Inc(a,b){Ofc();zlc.call(this,a,a.u[51]);this.a=b}function x2b(a, +b){var c;c=new w2b;h2b(c,a,b);return c.c.a}function E7b(a){if(!a)return new C7b(true);return D7b(a)}function buc(a,b){return b==null?GDc(a.d,null):$Dc(a.e,b)}function uWb(a){return a.d.a.length==0?a.f:DWb(a,0).d.b.c}function DOb(a,b,c,d){var e;a.d=true;e=x2b(c,d);a.Qh(b,e)}function DIc(a,b,c,d){var e;e=new wIc(b,c);e.c=d;EIc(a,e)}function EJc(a,b){mJc(a);return new KJc(a,new TJc(b,a.a))}function FJc(a,b){mJc(a);return new KJc(a,new YJc(b,a.a))}function gv(a){var b;return b=a.g,!b?a.g=new Oh(a,a.c): +b}function XA(a){return pF(a,21)?a.$d():new KJc(null,a.Zd())}function HBc(){HBc=JZ;EBc=new KBc;FBc=new KBc;GBc=new OBc}function yoc(){yoc=JZ;voc=new Boc;woc=new Foc;xoc=new Joc}function Wcb(){Wcb=JZ;Ucb=new Zcb;Tcb=new $cb;Vcb=new adb}function iE(){iE=JZ;var a,b;b=!nE();a=new vE;hE=b?new oE:a}function Asc(a,b){var c,d;c=a;d=b;return c==d?0:c0){c=c.f;--b}return c}function FB(a,b){var c; +c=frc(a.Fj);return b==null?c:c+": "+b}function ZC(a,b){var c,d;d=b.b;c=b.a;return d!=0?c:c+a.c.i.a}function x6(a){var b;if(a.G.zc){b=I6(a);Esc(b,a.A)||(a.A=b)}}function PIc(a,b){if(!BIc)return;LIc(a,(bIc(),aIc),b,null)}function GIc(a,b){if(!xIc)return;LIc(a,(bIc(),ZHc),b,null)}function JIc(a,b){if(!yIc)return;LIc(a,(bIc(),$Hc),b,null)}function OIc(a,b){if(!AIc)return;LIc(a,(bIc(),_Hc),b,null)}function Lyc(a){Cyc();return pF(a,69)?new gBc(a):new _zc(a)}function fab(){dab();return CE(xE(IL,1),sLc,263, +0,[cab,bab])}function ktb(){itb();return CE(xE(FO,1),sLc,268,0,[htb,gtb])}function nub(){lub();return CE(xE(KO,1),sLc,267,0,[kub,jub])}function Ygb(){Wgb();return CE(xE(UM,1),sLc,303,0,[Ugb,Vgb])}function Nwb(){Lwb();return CE(xE(rP,1),sLc,185,0,[Jwb,Kwb])}function lxb(){ixb();return CE(xE(yP,1),sLc,195,0,[gxb,hxb])}function uxb(){sxb();return CE(xE(BP,1),sLc,194,0,[qxb,rxb])}function aab(){$9();return CE(xE(HL,1),sLc,224,0,[Y9,X9,Z9])}function yFb(){wFb();return CE(xE(UQ,1),sLc,190,0,[vFb,uFb])} +function HJb(){FJb();return CE(xE(qR,1),sLc,271,0,[EJb,DJb])}function MJb(){KJb();return CE(xE(rR,1),sLc,270,0,[JJb,IJb])}function Jcb(a,b){var c;c=new $bb(a,b);_tc(Gcb,a,c);return c}function Kcb(a,b){var c;c=new _bb(a,b);_tc(Gcb,a,c);return c}function epb(a,b,c){var d;d=new dpb(a,c,new Ajb(a));Yob(d,b)}function slb(a,b,c,d,e,f){var g;g=plb(a,e);jlb(a,g,d,f,b,c)}function Wic(a,b,c,d,e){Ofc();Xic.call(this,a,b,c,d,e,false)}function Xbc(a,b,c,d){n8b();K9b.call(this,a,c,d);Ubc(this,b)}function Muc(a, +b){this.a=a;Guc.call(this,a);a.Yd();this.b=b}function $pc(a,b){this.a=new fxc;this.i=a;this.g=30;this.k=b}function tXb(a,b,c){dTb.call(this,(AZb(),gZb),a,b);this.a=c}function wKb(a,b){u9b(a,T9b(b.k));m9b(a,S9b(b.k));t9b(a,b.e)}function OAc(a,b){var c;c=a.b.ae(b);PAc(c,a.b.Yd());return c}function wuc(a){var b;a.c=a.a;b=a.a.Jd();a.b=vuc(a);return b}function Zfc(a,b){var c;return c=new ukc(false),Qfc(a,b,0,c)}function PAc(a,b){var c;for(c=0;c=0,lTc),1<=a.d)throw gZ(new jFc);return a.ce(a.c++)}function A5(a,b){if(b||kQb(l9(a),(iQb(),gQb)))return 1;return 0}function ru(a){var b;while(true){b=a.Jd();if(!a.Id())return b}}function D_(a){var b;b0((Afc(),Cdc),a);for(b=a.c;b;b=b.f)C_(b)}function M_(a){var b;b0((Afc(),Xdc),a);for(b=a.c;b;b=b.f)L_(b)}function Z5(a,b){Pd(V8b(a),aOc,a);Pd(V8b(b),aOc,b);I8b(a);I8b(b)}function o9(a,b){Xd(b!=(uab(),qab));Xd(b!=tab);a.Sb=b==rab?sab:b}function Fab(){Dab(); +return CE(xE(LL,1),sLc,226,0,[Aab,Cab,Bab])}function Lab(){Jab();return CE(xE(ML,1),sLc,223,0,[Gab,Iab,Hab])}function Rab(){Pab();return CE(xE(NL,1),sLc,222,0,[Mab,Nab,Oab])}function dbb(){bbb();return CE(xE(PL,1),sLc,227,0,[_ab,$ab,abb])}function rgb(){pgb();return CE(xE(OM,1),sLc,255,0,[ogb,mgb,ngb])}function Ycb(){Wcb();return CE(xE(fM,1),sLc,158,0,[Ucb,Tcb,Vcb])}function pqb(a){ppb();return a.n==(Afc(),Vcc)&&!!a.g&&a.g.n==Jdc}function Svb(a){hmb();this.b=a;kmb.call(this,a.a);this.a=new YEc}function Mob(a){var b; +b=Bwc(a.o)+a.n.a.length;Xd(b>0);return b-1}function dtb(a){var b;b=a.b.e>a.a;a.a=a.b.b.b;++a.b.b.b;return b}function fpb(a,b,c,d){var e;e=new dpb(a,b,new Ajb(a));cpb(e,c,d)}function jKb(a,b){return w8b((n8b(),new Wbc((Afc(),bfc),b)),a.s)}function ANb(a,b,c,d,e){return!c?null:w8b(new M9b(b,c,d,e),a.n)}function LNb(){JNb();return CE(xE(xR,1),sLc,252,0,[HNb,INb,GNb])}function oJb(){mJb();return CE(xE(oR,1),sLc,234,0,[lJb,jJb,kJb])}function gDb(){eDb();return CE(xE(rQ,1),sLc,230,0,[dDb,cDb,bDb])}function vWb(a){return wWb(a, +a.d.a.length==0?a.f:DWb(a,0).d.b.c)}function OWb(a){while(pXb(xWb(a,a.f)?zsc(a.b,a.f):0)>=0)yWb(a)}function VWb(a){while(qXb(xWb(a,a.f)?zsc(a.b,a.f):0)>=0)yWb(a)}function h9b(a,b){var c;c=i9b(a,a.j,b.f<<24>>24);c!=a.j&&(a.j=c)}function E8b(a,b){var c;c=Y8b(a,b);if(!c)return 0;return c.bi()}function Tbc(a,b){var c;c=new Vbc(a.n);c.a=a.a;return y8b(a,c,b)}function Ufc(a,b){var c;c=tu(b.ki().c.Vd());return Rnc(a.ki(),c)}function aFb(a,b,c){bFb.call(this,a,b,c,(EFb(),DFb),(wFb(),vFb))}function Vic(a){Ofc(); +Xic.call(this,a,null,null,false,null,true)}function EAb(){jsb.call(this,"es6NormalizeShorthandProperties")}function pzb(){jsb.call(this,"es6ConvertSuperConstructorCalls")}function Uzb(){jsb.call(this,"Es6RewriteBlockScopedDeclaration")}function Kqc(a){Iqc.call(this,a==null?jLc:NZ(a),pF(a,36)?a:null)}function ZIc(a){return a.replace(/[.?*+^$[\]\\(){}|-]/g,"\\$&")}function Dwc(a){jwc(this);rKc(this.a,Qrc($wnd.Math.max(8,a))<<1)}function fkc(){dkc();return CE(xE(RU,1),sLc,246,0,[ckc,akc,bkc])}function Jkc(){Hkc(); +return CE(xE(ZU,1),sLc,243,0,[Ekc,Fkc,Gkc])}function ucc(){scc();return CE(xE(JU,1),sLc,228,0,[qcc,rcc,pcc])}function Aoc(){yoc();return CE(xE(AV,1),sLc,164,0,[voc,woc,xoc])}function d$(){b$();return CE(xE(KK,1),sLc,174,0,[ZZ,a$,$Z,YZ,_Z])}function WZ(a,b){if(!a.d)return b;return(b$(),YZ).a+(""+b)+_Z.a}function Vg(a,b){var c;c=b.Ve();return new Wq(c,a.b.Ce(c,b.We()))}function dkb(a,b){var c;c=qkb(b);if(c)return mkb(a,c,b);return 0}function xZ(a){var b;if(pZ(a)){b=a;return b==-0?0:b}return aF(a)}function z8(a){var b; +if(!a.n){b=a.b.C;a.n=$Eb(b,a.i.a)}return a.n}function Vpb(a){var b,c;b=a;for(c=Hpb(b);c;c=Hpb(b))b=c;return b}function vnc(a,b,c,d){a.b=false;_tc(a.c,b,new ync(c,d));return a}function mTb(a,b){if(DWb(a.g,0).e==b)return iTb(a,b);return null}function SVb(a,b,c,d){!b?DOb(a.c,vWb(a.g),c,d):DOb(a.c,b.d.b,c,d)}function TVb(a,b,c,d){!b?DOb(a.c,vWb(a.g),c,d):DOb(a.c,b.o.b,c,d)}function aCb(a,b,c){ptc(ptc(ptc(ptc((a.a+="@",a),b)," {"),c),"}")}function Ruc(a,b,c){JKc(b,c,a.Yd());this.c=a;this.a=b;this.b=c- +b}function Lic(a){this.a=(bn(),mz(),lz);this.b=(dkc(),ckc);this.f=a}function wic(a){a.b=(dkc(),bkc);a.d=new J9b((Afc(),Jec));return a}function hmc(a){var b;Xd(a.Xi()!=null);b=Sd(a.Xi());return TKc(b)}function Pnc(a,b){var c;c=Znc(a,aoc(a.d,b));return Xkc(a.a,a.c,c)}function gnc(a,b,c){var d;d=a.b.Ie(b);!!d&&Wmc(c,d.a);a.b.Je(b,c)}function kwc(a,b){IKc(b);a.b=a.b-1&a.a.length-1;a.a[a.b]=b;pwc(a)}function lwc(a,b){IKc(b);a.a[a.c]=b;a.c=a.c+1&a.a.length-1;pwc(a)}function NHc(a,b){if(b==null)throw gZ(new msc); +return Utc(a.a,b)}function OHc(a,b){if(b==null)throw gZ(new msc);return Xtc(a.a,b)}function VIc(a,b){this.b=a;new $wnd.RegExp(a.source,"g");this.a=b}function G7b(){G7b=JZ;F7b=new $wnd.RegExp("^[a-zA-Z_$][\\w$]*$")}function H9(){H9=JZ;F9=new I9("BROWSER",0);G9=new I9("CUSTOM",1)}function a6(){a6=JZ;$5=new b6("INHERITS",0);_5=new b6("MIXIN",1)}function hz(a){Bn();this.a=(Cyc(),pF(a,69)?new gBc(a):new _zc(a))}function lqb(a){ppb();return a.n==(Afc(),Idc)||a.n==Gdc||a.n==Hdc}function V3(a){if(a.s){a.Lg(";"); +a._g();a.Ug();a.s=false}a.t=true}function Rd(a,b){if(a<0||a>=b)throw gZ(new Fqc(Hd(a,b)));return a}function Wd(a,b,c){if(a<0||bc)throw gZ(new Fqc(Jd(a,b,c)));}function il(a,b,c){!!c&&Kd(true);return new Gl(fJc(a).Ej(),b,1301)}function YJb(a,b){if(!xKb(b))return null;return XJb(a,UJb(a,b.o))}function PMb(a,b,c){a.o=!a.o?mOb(a.j):GMb(a);return a.o==b||a.o==c}function UZb(a,b,c){IZb.call(this,(n1b(),I_b),a);this.b=b;this.a=c}function XZb(a,b,c){IZb.call(this,(n1b(),L_b),a);this.b=b;this.a=c}function WZb(a, +b,c){IZb.call(this,(n1b(),K_b),a);this.a=b;this.b=c}function k$b(a,b,c){IZb.call(this,(n1b(),R_b),a);this.a=b;this.b=c}function u$b(a,b,c){IZb.call(this,(n1b(),__b),a);this.a=b;this.b=c}function t$b(a,b,c){IZb.call(this,(n1b(),$_b),a);this.b=b;this.a=c}function x$b(a,b,c){IZb.call(this,(n1b(),c0b),a);this.b=b;this.a=c}function z$b(a,b,c){IZb.call(this,(n1b(),e0b),a);this.b=b;this.a=c}function f_b(a,b,c){IZb.call(this,(n1b(),t0b),a);this.b=b;this.a=c}function g_b(a,b,c){IZb.call(this,(n1b(),u0b),a); +this.b=b;this.a=c}function i_b(a,b,c){IZb.call(this,(n1b(),w0b),a);this.a=b;this.b=c}function _$b(a,b,c){IZb.call(this,(n1b(),n0b),a);this.a=b;this.b=c}function k_b(a,b,c){IZb.call(this,(n1b(),y0b),a);this.b=b;this.a=c}function l_b(a,b,c){IZb.call(this,(n1b(),z0b),a);this.b=b;this.a=c}function o_b(a,b,c){IZb.call(this,(n1b(),D0b),a);this.b=b;this.a=c}function q_b(a,b,c){IZb.call(this,(n1b(),F0b),a);this.b=b;this.a=c}function w_b(a,b,c){IZb.call(this,(n1b(),L0b),a);this.b=b;this.a=c}function q1b(a, +b,c){IZb.call(this,(n1b(),N0b),a);this.b=b;this.a=c}function y1b(a,b,c){IZb.call(this,(n1b(),V0b),a);this.b=b;this.a=c}function z1b(a,b,c){IZb.call(this,(n1b(),W0b),a);this.b=b;this.a=c}function r1b(a,b,c){IZb.call(this,(n1b(),O0b),a);this.a=b;this.b=c}function F1b(a,b,c){IZb.call(this,(n1b(),b1b),a);this.a=b;this.b=c}function I1b(a,b,c){IZb.call(this,(n1b(),a1b),a);this.a=b;this.b=c}function M1b(a,b,c){IZb.call(this,(n1b(),i1b),a);this.a=b;this.b=c}function J1b(a,b,c){IZb.call(this,(n1b(),e1b),a); +this.b=b;this.a=c}function P1b(a,b,c){IZb.call(this,(n1b(),k1b),a);this.b=b;this.a=c}function Q1b(a,b,c){IZb.call(this,(n1b(),l1b),a);this.b=b;this.a=c}function R1b(a,b,c){IZb.call(this,(n1b(),m1b),a);this.b=b;this.a=c}function F4(a,b){var c;c=H8b(b,(n8b(),Y7b));!!c&&a.fh(c,(_4(),W4))}function kbb(a,b){var c,d;for(d=b.Vd();d.Id();){c=d.Jd();jbb(a,c)}}function Xf(a,b){var c,d;c=Cx(a.c,b);if(c){d=c.Yd();c.Rd();a.d-=d}}function YSb(a,b){var c;c=Cxc(a.a,b);if(c>=0)return c;return-c-2}function z4b(a,b){if(!a.b|| +!a.b.e)return null;return pEc(a.b.e,b)}function L4b(a,b){if(!a.b||!a.b.j)return null;return pEc(a.b.j,b)}function H4b(a,b){if(!a.d||!a.d.o)return null;return pEc(a.d.o,b)}function lnc(a,b,c,d,e){if(a.a)return false;return Sic(a,b,c,d,e)}function ykc(a,b,c){!a.a&&(a.a=new ukc(a.c));return Qfc(b,c,0,a.a)}function ujc(a){ae(a.oi()||a.f==(dkc(),bkc),lRc,a);return cgc(a.k)}function ntc(a,b,c,d){a.a+=""+Vsc(b==null?jLc:NZ(b),c,d);return a}function Gsb(a){var b;b=new Esb;q4(new L4(b),a,(_4(),W4));return b} +function vGc(a,b){var c;c=1-b;a.a[c]=wGc(a.a[c],c);return wGc(a,b)}function Uxc(a,b){var c;for(c=0;c0}function e5b(a,b){if(b.d){!a.d&& +(a.d=new L5b);a.d.p|=!b.d?0:b.d.p}}function fGc(a){return!a.a?a.c:a.e.length==0?a.a.a:a.a.a+(""+a.e)}function Rkc(a,b){return Zkc(a,CE(xE(cV,1),xPc,23,0,[b,a.u[32]]))}function $kc(a,b){return oEc(a.f,b)?pEc(a.f,b).Le():(Bn(),Bn(),An)}function u4(a,b){Yd(b.n==(Afc(),Vcc),b);a.c.Pg();s4(a,b.c);a.c.Rg()}function K$(a){var b;b=(Xd(H3b(a)),new L9b((Afc(),Pcc),a));return b}function f_(a){var b;b=(Xd(H3b(a)),new L9b((Afc(),zfc),a));return b}function fsb(a,b,c){var d;d=c.g;Xd(!!d);Xd(M8b(d,b));fpb(a.b,a, +b,c)}function XD(a,b,c){var d;d=VD();try{return UD(a,b,c)}finally{YD(d)}}function Psc(a,b,c){c=dtc(c);return a.replace(new RegExp(b,"g"),c)}function Tjb(a,b,c){Rjb();u7(a,Lmb(b,Pjb,CE(xE(nW,1),uNc,2,6,[c])))}function Ujb(a,b,c){Rjb();u7(a,Lmb(b,Qjb,CE(xE(nW,1),uNc,2,6,[c])))}function wsb(a,b,c){kwc(a.f,new Dsb(b,c));Esc(b,iOc)||(a.d.a=false)}function Swb(a,b){a.a=Esc(b.substr(0,1),cRc)?b.substr(1):b;a.d=null}function oQb(a,b){if(a.a.Sd(b))return a;return new uQb(vQb(a.a,b))}function ZSb(a,b){var c; +c=YSb(a,b);return new X1b(a.b,b,c,b-a.a[c])}function i3b(a,b,c){Yd(H3b(b),b);Yd(H3b(c),c);return new N9b(a,b,c)}function z2b(a){if(a.b>=a.c)throw gZ(G2b(a));return zsc(a.a,a.b++)}function S4b(a,b){if(!a.d||!a.d.o)return false;return oEc(a.d.o,b)}function a5b(a,b){if(!a.d.r)return false;return _wc(a.d.r,b,0)!=-1}function C8(a){if(!a.a||!a.a.sh())return;Twb(a.a.sh(),(scc(),pcc))}function F3b(a){Xd(a.length!=0);return n8b(),new Wbc((Afc(),fec),a)}function YMb(a,b){return b==(fOb(),$Nb)?QMb(a,(Afc(), +Pec)):WMb(a,b)}function Ykc(a,b,c){return new koc(a,b,(Bn(),Sd(c),In(new eyc(c))))}function Jyc(a){Cyc();var b;b=new nDc;b.a.Je(a,b);return new JAc(b)}function H8b(a,b){var c;c=Y8b(a,b);if(!c)return null;return c.ci()}function Unc(a,b){var c;c=Snc(a,b);return c==-1?a.a.u[51]:a.d.ce(c)}function hJc(a,b){if(a.a<=a.b){b.kf(a.a++);return true}return false}function aAb(a){this.a=a;jsb.call(this,"Es6RewriteDestructuring")}function Fvb(a){Avb();Gvb.call(this,a,Nvb(Z2b("js/polyfills.txt")))}function Vnb(){TZ(); +XZ.call(this,null);Snb(this);this.a=(Lwb(),Jwb)}function YJc(a,b){QFc.call(this,b.ff(),b.ef()&-6);this.a=a;this.b=b}function vl(a,b,c,d){this.e=d;this.d=null;this.c=a;this.a=b;this.b=c}function KDc(a){this.e=a;this.b=this.e.a.entries();this.a=new Array}function JGc(a){KGc.call(this,a,(oHc(),kHc),null,false,null,false)}function PGc(a){QGc.call(this,a,(oHc(),kHc),null,false,null,false)}function TWb(a){while(Uqc(xWb(a,a.f)?zsc(a.b,a.f):0,16)>=0)yWb(a)}function u_(a,b){var c;b0((Afc(),Vec),b);for(c=b.c;c;c= +c.f)o0(a,c)}function Q_(a,b){var c;b0((Afc(),dec),b);for(c=b.c;c;c=c.f)P_(a,c)}function e0(a,b){var c;b0((Afc(),Eec),b);for(c=b.c;c;c=c.f)f0(a,c)}function __(a,b){switch(b.n.f){case 29:W_(b);break;case 26:I_(a,b)}}function Qhb(a,b,c){if(b){$hb(a,null,b);nib(a.j,b,Ghb(a,b,false,c))}}function Yf(a,b,c,d){return pF(c,69)?new zi(a,b,c,d):new ri(a,b,c,d)}function Ag(a,b){return uF(b)===uF(a)?"(this Map)":b==null?jLc:NZ(b)}function r9(a){return a.cb.Ed(a.Tb).Fd((Nqc(),vab(a.Sb)?true:false))}function UC(a){htc(a.b, +34);bD(new dD(a.j),a);htc(a.b,59);htc(a.b,34)}function E5(a){if(a.o<=0&&a.u!=a.v+1)return;itc(a.j,10);++a.n;a.o=0}function Q3b(a){Xd(a.n==(Afc(),rec)||a.n==Sec);return new L9b(Jec,a)}function Ydb(a){var b;b=E7b(H8b(a,(Hbc(),mbc)));x6b(b);q9b(a,k6b(b))}function Ubb(){Sbb();return CE(xE(YL,1),sLc,193,0,[Obb,Rbb,Qbb,Pbb])}function PFb(){NFb();return CE(xE(XQ,1),sLc,191,0,[JFb,KFb,LFb,MFb])}function rWb(){pWb();return CE(xE(PR,1),sLc,202,0,[oWb,nWb,lWb,mWb])}function $$b(){Y$b();return CE(xE(QS,1),sLc, +206,0,[V$b,W$b,X$b,U$b])}function sHc(){oHc();return CE(xE(lY,1),sLc,148,0,[kHc,lHc,mHc,nHc])}function d9(){b9();return CE(xE(BL,1),sLc,160,0,[a9,Z8,Y8,X8,_8,$8])}function In(a){Bn();var b;if(pF(a,66)){b=a;return b.sf()}return Gn(a)}function tlc(a,b){var c,d;c=b.e;d=c.Ni(a);if(d!=c)return d;return b}function Ffb(a,b){var c;c=K3b(b.a);a.f.d&&r9b(c,pfb(a.f,38));return c}function Z$(a){var b,c,d;b=a_(Se(Ze(),MMc));d=(c=Tqb(b,a),c);return d}function A2b(a){while(I2b(a.bb)throw gZ(new Fqc(Id(a,b,qLc)));return a}function g1(a,b){if(!b)throw gZ(new nsc("Null kind"));a.b=b;return a}function d5b(a){if(!a.c)return false;!a.b&&(a.b=new F5b);return true}function J4b(a){if(!a.d||!a.d.r)return Bn(),Bn(),An;return In(a.d.r)}function O4b(a){if(!a.d|| +!a.d.t)return oo(),oo(),no;return wo(a.d.t)}function xu(a){var b;Sd(a);if(pF(a,105)){b=a;return b}return new yu(a)}function OCc(a){var b;b=lKc(a.b,a.b.length);return new TCc(a.a,b,a.c)}function Nv(a,b){var c;c=Lyc(Nw(new Bw(a,b)));ou(new Bw(a,b));return c}function uz(a,b){var c;c=false;while(b.Id())c=c|a.Wd(b.Jd());return c}function A6(a,b,c){var d;d=yOb(b,a.G.nc,(FJb(),EJb),a.G.mc,c);return d}function zhc(a,b,c,d){var e;e=a.cj(b,c,false,d);jlc(a.A,b,a);return e}function Lbb(a,b){var c;c=b.b-a.b; +if(c!=0)return c;c=a.c-b.c;return c}function H4(a,b){if(b.n==(Afc(),_cc))return H4(a,b.c);return arb(b.n)}function Jpb(a){ppb();Yd(a.n==(Afc(),Xec),a);return H8b(a,(n8b(),V7b))}function Ht(a){wt();return Nk(),Sd(a),new $Ic(new cl(a),new Qk,new Sk)}function oMb(a,b,c){return!c||c.Ud()?dKb(a.a,(Afc(),tdc)):nMb(a,b,c)}function nGb(a,b,c,d,e){XFb();bGb.call(this,a,b,null,d,e);this.a=wo(c)}function oPb(a,b,c,d,e){this.f=a;this.b=b;this.a=c;this.d=d;this.c=e}function JDb(a,b){if(!b)throw gZ(new nsc("Null type")); +a.c=b;return a}function LTb(a){if(zVb(a,0,(AZb(),aZb)))return jVb(a);return KTb(a,1)}function A4b(a){if(!a.d||!a.d.f)return Bn(),Bn(),An;return Lyc(a.d.f)}function C4b(a){if(!a.d||!a.d.g)return Bn(),Bn(),An;return Lyc(a.d.g)}function K4b(a){if(!a.d||!a.d.s)return Bn(),Bn(),An;return Lyc(a.d.s)}function W1b(a){if(!a.d)return"";return Ut(Se(Ye(new nc(47)),a.d.c))}function PEb(a,b){var c;c=SEb(a,b);if(c)return c;throw gZ(new lGb(b));}function RSb(a,b){OSb();var c;c=Po(oSb,a);return!!c&&(b||!c.a)?c:null} +function P8b(a){var b,c;b=0;for(c=a.c;!!c&&b<=3;c=c.f)++b;return b==3}function qjc(a){var b;b=new mDc;a.oi()?Rc(b,tjc(a)):Rc(b,a.c);return b}function bIc(){bIc=JZ;ZHc=new fIc;$Hc=new iIc;_Hc=new lIc;aIc=new oIc}function CIc(){CIc=JZ;zIc=true;xIc=false;yIc=false;BIc=false;AIc=false}function RIc(a){CIc();if(zIc)return new QIc(null);return tIc(vIc(),a)}function mwc(a){if(a.b==a.c)return;a.a=zE(iW,mLc,1,8,5,1);a.b=0;a.c=0}function mC(a,b){b.a.length==0?Ywc(a.i.c,null):Ywc(a.i.c,b);++a.b;a.c=0}function O$(a, +b){var c;c=x3b(a,(n8b(),new Wbc((Afc(),bfc),b)));return c}function tf(a){Sd(a);if(pF(a,25))throw gZ(a);if(pF(a,80))throw gZ(a);}function YD(a){a&&dE((bE(),aE));--QD;if(a)if(SD!=-1){$D(SD);SD=-1}}function n7(a){if(a.G.ad){H7(a);m9(a.G,wab(a.G.Sb))&&G7(a)}else y6(a)}function xpb(a){ppb();var b,c;c=1;for(b=a.c;b;b=b.f)c+=xpb(b);return c}function _bb(a,b){Wbb();var c;this.a=a;this.b=(bn(),c=new eyc(b),mp(c))}function Bkc(a,b,c){this.d=a;this.b=b;this.c=c;this.a=31*Xfc(b)+Xfc(c)}function PSb(a,b,c,d){he.call(this, +a,b);this.c=c;this.b=d;this.a=VSb(d)}function Lq(a){this.e=a;this.c=this.e.a;this.b=this.e.g;this.d=this.e.i}function rqb(a){switch(a.n.f){case 86:case 87:return true}return false}function n4b(a,b){if(J4b(a).Ud()){x5b(a,b,hMc);return true}return false}function w3b(a,b){Xd(H3b(a));Xd(H3b(b));return new N9b((Afc(),Odc),a,b)}function ekb(a,b,c,d){if(!b||b==c)return;ekb(a,b.f,c,d);fkb(a,b.c,c,d)}function hkc(a,b,c,d){Ofc();Wic.call(this,a,null,null,c,d);this.a=Sd(b)}function ecc(a,b){n8b();J9b.call(this, +(Afc(),jfc));this.a=a;ccc(this,b)}function Dsc(a,b){var c;c=b.length;return Esc(a.substr(a.length-c,c),b)}function xyc(a){var b;b=a.length-1;for(;b>=0&&(a[b]|0)==0;--b);return b}function _Tb(a){var b;iTb(a,(AZb(),HYb));b=KTb(a,1);iTb(a,MXb);return b}function DJc(a,b){var c;return b.b.Od(GJc(a,b.c.Cd(),(c=new iKc(b),c)))}function Hhb(a,b,c){return aib(a,c,PPc,CE(xE(HU,1),IMc,7,0,[mib(b,c)]))}function c5(){_4();return CE(xE(eL,1),sLc,136,0,[$4,U4,Z4,V4,X4,Y4,W4])}function nwb(a){a=ZIc(a);return YIc(Psc(a, +"\\{\\d+\\}","\\\\E.*\\\\Q"))}function lub(){lub=JZ;kub=new mub(GMc,0);jub=new mub("ALL_UNQUOTED",1)}function KJb(){KJb=JZ;JJb=new LJb("STRICT",0);IJb=new LJb("SLOPPY",1)}function E2(){E2=JZ;B2=new F2(zMc,0);D2=new F2(AMc,1);C2=new F2(GMc,2)}function cEc(a){this.d=a;this.b=this.d.a.entries();this.a=this.b.next()}function Kz(a,b){this.c=a;this.d=b;this.a=this.c.Vd();this.b=this.d.Vd()}function Bw(a,b){var c;this.f=a;this.b=b;c=Xtc(a.b,b);this.c=!c?null:c.b}function TJc(a,b){QFc.call(this,b.ff(),b.ef()& +-16449);this.a=a;this.c=b}function tEc(){fDc.call(this);mEc(this);this.b.b=this.b;this.b.a=this.b}function uEc(){fuc.call(this);mEc(this);this.b.b=this.b;this.b.a=this.b}function WD(b){TD();return function(){return XD(b,this,arguments);var a}}function PD(){if(Date.now)return Date.now();return(new Date).getTime()}function Su(a){if(Ru(a)){a.c=a.a;return a.a.Jd()}else throw gZ(new jFc);}function Oy(a,b){if(Zi(b).Ud())return false;qj(b,new Sy(a));return true}function Ldb(a,b){var c;a.a=false;c=new Odb(a); +epb(a.b.a,b,c);return a.a}function gs(a,b){var c,d;for(d=b.Vd();d.Id();){c=d.Jd();a.Mf(c)}return a}function qtb(a,b){var c;ptb(a,b);for(c=b.c;c;c=c.f){Xd(c.g==b);qtb(a,c)}}function t_(a,b,c){var d;b0((Afc(),cdc),b);for(d=b.c;d;d=d.f)s_(a,d,c)}function FWb(a,b,c){EWb(a,wWb(a,a.d.a.length==0?a.f:DWb(a,0).d.b.c),b,c)}function zDb(a){wDb();return GDb(JDb(IDb(HDb(new KDb,a),a),(MEb(),IEb)))}function BDb(a){wDb();return GDb(JDb(IDb(HDb(new KDb,a),a),(MEb(),KEb)))}function OEb(){MEb();return CE(xE(KQ,1), +sLc,172,0,[KEb,JEb,LEb,HEb,IEb])}function g$b(){e$b();return CE(xE(nS,1),sLc,179,0,[_Zb,c$b,b$b,d$b,a$b])}function g6b(){e6b();return CE(xE(rU,1),sLc,177,0,[b6b,a6b,c6b,d6b,_5b])}function Zab(){Xab();return CE(xE(OL,1),sLc,169,0,[Sab,Vab,Tab,Wab,Uab])}function Cqb(a){ppb();return!!a&&(a.n==(Afc(),ufc)||a.n==jec||a.n==gdc)}function Gf(a){a.f=3;a.e=Jz(a);if(a.f!=2){a.f=0;return true}return false}function h6b(a,b){if(o4b(a.a,b)){a.e=true;return true}else return false}function j6b(a,b){if(s4b(a.a,b)){a.e= +true;return true}else return false}function K6b(a,b){if(q4b(a.a,b)){a.e=true;return true}else return false}function B6b(a,b){if(i5b(a.a,b)){a.e=true;return true}else return false}function j7b(a,b){if(t4b(a.a,b)){a.e=true;return true}else return false}function o7b(a,b){if(k4b(a.a,b)){a.e=true;return true}else return false}function x7b(a,b){if(v4b(a.a,b)){a.e=true;return true}else return false}function sEc(a,b){var c;c=auc(a.c,b);if(c){FEc(c);return c.e}return null}function Qsc(a,b,c){var d;c=dtc(c); +d=new RegExp(b);return a.replace(d,c)}function Fxc(a,b){var c,d;c=(d=a.slice(0,b),DE(d,a));c.length=b;return c}function Iic(a,b){a.k=Xkc(a.f,(Bn(),Sd(b),In(new eyc(b))),null);return a}function D3b(a){var b;b=Z3b((Afc(),$dc),a);c9b(b,(n8b(),_7b),0);return b}function hB(a,b){var c;if(pF(b,247)){c=b;return Hxc(a.c,c.c)}return false}function pC(a,b){if(EC(a.a)&&GC(a.a)==b){FC(a.a);return true}return false}function t8(a,b){if(_wc(a.d,b,0)==-1){Ywc(a.d,b);return true}return false}function u8(a,b){if(_wc(a.o, +b,0)==-1){Ywc(a.o,b);return true}return false}function Sb(a){a.i=3;a.g=a.Gd();if(a.i!=2){a.i=0;return true}return false}function FE(a){var b,c,d;b=a&sMc;c=a>>22&sMc;d=a<0?tMc:0;return HE(b,c,d)}function Fhb(a,b){var c;c=F9b(new J9b((Afc(),Xcc)),b);Ywc(a.q,c);return c}function nfb(a,b,c){var d;d=i3b((Afc(),edc),b,c);a.d&&r9b(d,c.d);return d}function jjb(a){return a.n==(Afc(),_ec)&&_8b((Xd(!!a.c&&!a.c.f),a.c),BMc)}function Nhb(a,b){return F9b(Yjb(K3b(OPc+(a.r.b==0?"":"$"+a.r.b)),a.g),b)}function Uqb(a){ppb(); +return F9b((Yd(H3b(a),a),new L9b((Afc(),Ddc),a)),a)}function wDb(){wDb=JZ;vDb=GDb(JDb(IDb(HDb(new KDb,kNc),kNc),(MEb(),KEb)))}function itb(){itb=JZ;htb=new jtb("LEGACY",0);gtb=new jtb("EXPORT_ALL",1)}function x3b(a,b){Xd(H3b(a));Xd(b.n==(Afc(),bfc));return new N9b(Pdc,a,b)}function B3b(a,b){Xd(H3b(a));Xd(b.n==(Afc(),Vcc));return new N9b(Tdc,a,b)}function agc(a,b){return b==0?Pfc(a,new stc,true).a:a.di(new stc,true).a}function Skc(a,b){return Zkc(a,CE(xE(cV,1),xPc,23,0,[b,a.u[52],a.u[32]]))}function iJc(a, +b){this.c=0;this.b=b;IFc.call(this,a,17493);this.a=this.c}function hGc(a,b,c){this.b=a;this.d=b;this.e=c;this.c=this.d+(""+this.e)}function dA(a){this.d=a;this.c=new TEc(new KEc(this.d.a));this.a=(Zu(),Yu)}function QIc(a){CIc();if(zIc)return;this.c=a;this.e=true;this.a=new fxc}function Nbc(a){var b;if(!a.a)throw gZ(new jFc);b=a.a;a.a=a.a.f;return b}function fJb(a,b){if(!b)throw gZ(new nsc("Null runMode"));a.f=b;return a}function f1(a,b){if(!b)throw gZ(new nsc("Null jsDocInfo"));a.a=b;return a}function Td(a, +b){if(a==null)throw gZ(new nsc(b==null?jLc:NZ(b)));return a}function C1(a,b){var c;nq(a.a);a.c=a.b.b;c=x8b(b,false);A1(a,b,c);return a}function lrc(a,b,c){var d;d=jrc(a);yrc(a,d);d.i=c?8:0;d.f=b;d.e=c;return d}function Dn(a){var b;b=(Sd(a),a?new hxc(a):Nw(a.Vd()));Hyc(b);return Vn(b)}function o_(a,b){var c;b0((Afc(),Ycc),b);U_(b);for(c=b.c;c;c=c.f)E_(a,c)}function a0(a,b){var c;b0((Afc(),xec),b);U_(b);for(c=b.c;c;c=c.f)E_(a,c)}function n_(a,b){var c;b0((Afc(),Vcc),b);for(c=b.c;c;c=c.f)q0(a,c,false)} +function $Bb(a,b,c){M4.call(this,a,b);Sd(c);this.b=c;this.a=new Emb(false)}function Smb(a){this.d=new fxc;this.a=new fxc;this.e=a;this.b=-1;this.c=-1}function nob(){mob(this);this.i=null;this.e=true;this.g=new Fr;this.f=this}function Qzb(){jsb.call(this,"Es6RewriteBlockScopedFunctionDeclaration")}function ugc(a,b){Ofc();var c;return!a?!b:(c=new ukc(false),Qfc(a,b,0,c))}function TSb(a,b){OSb();var c;return c=Po(oSb,a),!!(!!c&&(b||!c.a)?c:null)}function D5b(a,b){if(a)return b?new H7b(x8b(a.a,false), +a.b):a;return null}function yDb(a,b){wDb();return GDb(JDb(IDb(HDb(new KDb,b),a),(MEb(),HEb)))}function ADb(a,b){wDb();return GDb(JDb(IDb(HDb(new KDb,b),a),(MEb(),JEb)))}function GWb(a,b,c){EOb(a.e,wWb(a,a.d.a.length==0?a.f:DWb(a,0).d.b.c),b,c)}function DWb(a,b){while(a.d.a.length<=b)Ywc(a.d,MWb(a));return $wc(a.d,b)}function Ajc(a,b){if(Esc(aNc,b)){zjc(a);return a.g}else return fnc(a.t,b)}function pEc(a,b){var c;c=Xtc(a.c,b);if(c){rEc(a,c);return c.e}return null}function n3b(a,b){var c;c=Z3b((Afc(), +_cc),a);d9b(c,(Hbc(),mbc),b);return c}function yLb(a,b){var c;c=dKb(a.a,(Afc(),Ddc));q8b(c,yKb(a.a,b.a));return c}function eGc(a,b){!a.a?a.a=new vtc(a.d):ptc(a.a,a.b);mtc(a.a,b);return a}function pt(a,b){var c,d;for(d=new yuc(b.a);d.b;){c=wuc(d);fs(a,c)}return a}function TB(a){if(a<=0)throw gZ(new qnb("x ("+a+") must be > 0"));return a}function aEb(a,b){if(b==null)throw gZ(new nsc("Null name"));a.d=b;return a}function w8b(a,b){Yd(!a.j,"Node has existing properties.");a.j=b.j;return a}function x7(a, +b){Td(b,"the error manager cannot be null");a.g=new Yxb(b)}function h3b(a,b){Yd(X8b(a),a);Yd(H3b(b),b);return new N9b((Afc(),Ccc),a,b)}function p3b(a,b){Yd(H3b(a),a);Yd(H3b(b),b);return new N9b((Afc(),fdc),a,b)}function Avc(a,b){var c,d;c=b.Ve();d=a.wj(c);return!!d&&kFc(d.We(),b.We())}function blc(a,b){var c;Ld(b.indexOf("<")==-1,CTc);return c=a.t,eq(a.B,c,b)}function dnc(a){var b;if(!a.a)return null;b=a.a.Si();return!b?null:b.Vi()}function oC(a){var b;if(!EC(a.a))return true;b=GC(a.a);return b== +59||b==44}function Qs(a,b){var c;if(pF(b,21)){c=b;a.b.a.length+c.Yd()}Qr(a,b);return a}function oFc(a,b){var c;c=R2b(a.a,b);if(c==null)throw gZ(new iFc);return c}function ne(a,b){var c;c=a[":"+b];FKc(!!c,CE(xE(iW,1),mLc,1,5,[b]));return c}function ae(a,b,c){if(!a)throw gZ(new Irc(rf(b,CE(xE(iW,1),mLc,1,5,[c]))));}function Pd(a,b,c){if(!a)throw gZ(new qnb(rf(b,CE(xE(iW,1),mLc,1,5,[c]))));}function KKc(a,b,c){if(a<0||b>c||b>24;d=a.j;while(!!d&&c!=d.c)d=d.b;return d}function xgc(a){var b;if(a.Li()){b=a.Li();return Wnc(b.B,alc(b.A))}return false}function Sxc(a,b,c,d){var e; +d=(HBc(),!d?EBc:d);e=a.slice(b,c);Txc(e,a,b,c,-b,d)}function uNb(a,b,c,d,e){var f,g;if(d){g=a.j.g;f=lOb(a.j);q6b(a.f,d,b,c,g,f,e)}}function ce(a,b,c,d){if(!a)throw gZ(new Irc(rf(b,CE(xE(iW,1),mLc,1,5,[c,d]))));}function Ulb(a,b,c,d,e,f){this.c=c;this.b=d;this.a=a;this.g=e;this.e=b;this.d=f}function OPb(a,b,c,d,e,f){he.call(this,a,b);this.d=c;this.c=d;this.b=e;this.a=f}function ID(a){AB(this);this.g=!a?null:FB(a,a.Yf());this.f=a;BB(this);this.Zf()}function y1(a){var b,c;for(c=In(a.c).Vd();c.Id();){b= +c.Jd();a.zg(b.b,b.a)}a.yg()}function m_(a,b){var c;c=C8b(b);2!=c&&D0(PMc+c,b);E_(a,b.c);E_(a,b.c?b.c.i:null)}function kPb(a,b){a.a.Bh(URc+occ(rSc,CE(xE(iW,1),mLc,1,5,[b])),a.b.ug(),a.d,a.c)}function lPb(a,b){a.a.Bh(URc+occ(jSc,CE(xE(iW,1),mLc,1,5,[b])),a.b.ug(),a.d,a.c)}function mPb(a,b){a.a.Bh(URc+occ(mSc,CE(xE(iW,1),mLc,1,5,[b])),a.b.ug(),a.d,a.c)}function nPb(a,b){a.a.Bh(URc+occ(qSc,CE(xE(iW,1),mLc,1,5,[b])),a.b.ug(),a.d,a.c)}function qDb(){oDb();return CE(xE(sQ,1),sLc,145,0,[jDb,kDb,lDb,hDb,mDb, +nDb,iDb])}function kWb(){iWb();return CE(xE(NR,1),sLc,149,0,[bWb,cWb,dWb,eWb,fWb,hWb,gWb])}function $9b(a,b){n8b();return a<0||b<0?-1:(b&-4096)!=0?a<<12|xMc:a<<12|b&xMc}function Jab(){Jab=JZ;Gab=new Kab(COc,0);Iab=new Kab(qOc,1);Hab=new Kab(DOc,2)}function oHc(){oHc=JZ;kHc=new pHc("All",0);lHc=new tHc;mHc=new vHc;nHc=new yHc}function Ofc(){Ofc=JZ;Nfc=(bn(),pp(CE(xE(iW,1),mLc,1,5,[NMc,"IArrayLike",kPc])))}function gub(){hub.call(this,"REGISTER_BOOLEAN",0,"goog.tweak.registerBoolean")}function yuc(a){this.e= +a;this.d=new cEc(this.e.e);this.a=this.d;this.b=vuc(this)}function tyc(a){var b;b=((a-1)/31|0)+1;this.a=zE(yF,iMc,46,0,15,1);rKc(this.a,b)}function GJc(a,b,c){var d;lJc(a);d=new fKc;d.a=b;a.a.Hd(new jKc(d,c));return d.a}function _wc(a,b,c){for(;c0)return _Kc;if(iZ(a,hMc)<0)return hMc;return yZ(a)}function aF(a){if(UE(a,(iF(),hF))<0)return-QE(XE(a));return a.l+a.m*vMc+a.h*wMc}function Xrc(a,b){var c;if(b==10||b<2||b>36)return""+a;return c=a,c.toString(b)}function HJc(a,b){mJc(a);if(iZ(b,0)==0)return a;return new KJc(a,new aKc(b,a.a))}function NCc(a,b){var c;c=b.f;if(!a.b[c]){a.b[c]=b;++a.c;return true}return false}function JEc(a,b){var c;if(IEc(a,b)){c=b.Ve();sEc(a.a,c);return true}return false}function k$(a,b){var c;c=a;while(c){if(oEc(c.e, +b))return true;c=c.b}return false}function Qr(a,b){var c,d;Sd(b);for(d=b.Vd();d.Id();){c=d.Jd();a.Jf(Sd(c))}return a}function xg(a,b){var c,d;for(d=b.He().Vd();d.Id();){c=d.Jd();a.Je(c.Ve(),c.We())}}function gTb(a,b){var c,d;for(d=b.b.Vd();d.Id();){c=d.Jd();!c.b&&uTb(a,b.a,c.c)}}function mu(a,b){var c;Sd(a);Sd(b);c=false;while(b.Id())c=c|a.Pd(b.Jd());return c}function Wsc(a){var b,c;c=a.length;b=zE(xF,eLc,46,c,15,1);Gsc(a,0,c,b,0);return b}function hac(a){var b;if(!a.a.a)throw gZ(new jFc);b=a.a.a; +a.a.a=a.a.a.g;return b}function W0(a,b){if(!b)throw gZ(new nsc("Null fullClassNameNode"));a.g=b;return a}function cJb(a,b){if(!b)throw gZ(new nsc("Null jsDocParsingMode"));a.c=b;return a}function FKc(a,b){if(!a)throw gZ(new qnb(LKc("Enum constant undefined: %s",b)));}function Nk(){Nk=JZ;Mk=new $Ic(new Ok,new Uk,new Wk);new $Ic(new Yk,new $k,new al)}function eDb(){eDb=JZ;dDb=new fDb(GMc,0);cDb=new fDb("LOCAL",1);bDb=new fDb(COc,2)}function G$(){G$=JZ;E$=new H$(GMc,0);F$=new H$("UNMAPPED",1);D$=new H$("MAPPED", +2)}function iF(){iF=JZ;eF=HE(sMc,sMc,524287);fF=HE(0,0,uMc);gF=FE(1);FE(2);hF=FE(0)}function Leb(){Leb=JZ;Keb=rQb((iQb(),TPb),CE(xE(FR,1),sLc,24,0,[(IRb(),SQb),PQb]))}function sjb(){sjb=JZ;rjb=rQb((iQb(),TPb),CE(xE(FR,1),sLc,24,0,[(IRb(),EQb),lRb]))}function wUb(a){var b,c;c=DWb(a.g,0).d.b;b=jTb(a);return new c_b(new Z1b(c,a.f),b)}function mp(a){bn();var b;if(pF(a,135)&&!pF(a,248)){b=a;return b}return np(a.Vd())}function p6b(a,b,c,d,e,f){var g;if(a.b){g=new W5b;g.d=b;S5b(g,c,d,e,f);N5b(a.b,g)}}function dE(a){var b, +c;if(a.b){c=null;do{b=a.b;a.b=null;c=gE(b,c)}while(a.b);a.b=c}}function cE(a){var b,c;if(a.a){c=null;do{b=a.a;a.a=null;c=gE(b,c)}while(a.a);a.a=c}}function ZLb(a,b){var c;c=dKb(a.a,(Afc(),Uec));!!b.a&&q8b(c,yKb(a.a,b.a));return c}function D2b(a,b){var c,d;c=z2b(a);b.b=c;if(c==116||c==84){d=z2b(a);b.c=d}return b}function D8b(a,b){var c,d;d=a.c;c=0;while(d){if(b==d)return c;d=d.f;++c}return-1}function _C(a,b){var c,d,e,f;e=a.a.b;f=b.e.b;c=a.a.a;d=b.e.a;return e==f&&c>=d||e>f}function erb(a,b,c){ppb(); +var d;b.Ag(a);if(c.Ld(a))for(d=a.c;d;d=d.f)erb(d,b,c)}function mXb(a){switch(a){case 8232:case 8233:return false;default:return jXb(a)}}function Vpc(a){if(!a.j){a.j=Zpc(a);!a.j&&(a.j=new Woc(a.i,In(Wpc(a))))}return a.j}function Z0(a,b){if(!b)throw gZ(new nsc("Null superClassNameNode"));a.k=b;return a}function UMb(a,b,c,d,e,f){var g;f?g=qNb(a,b):g=nNb(a,b);uNb(a,c,d,g,e);return g}function YTb(a,b){var c;c=(Bn(),new Zr);while(DVb(a))Xr(c,XTb(a,b));return In(c.a)}function E3b(a,b){var c;Xd(a.n==(Afc(), +fec));Xd(I3b(b));c=new N9b(eec,a,b);return c}function IWb(a,b){if((xWb(a,a.f)?zsc(a.b,a.f):0)==46){yWb(a);RWb(a)}return HWb(a,b)}function L6b(a){if((a.a.a&32)!=0)return false;else{a.a.a|=32;a.e=true;return true}}function e7b(a){if((a.a.a&64)!=0)return false;else{a.a.a|=64;a.e=true;return true}}function uOb(a){if((a&57296)!=0)return false;return a==10||a==13||a==8232||a==8233}function u9(a){if(a.zb==($9(),X9))return false;return a.Dc||kQb((iQb(),YPb),l9(a))}function qq(a,b){return Ex(wq(a,b,yZ(rZ(VLc, +Vrc(yZ(rZ(b==null?0:Db(b),WLc)),15)))))}function Xsc(a,b){return b==(bFc(),bFc(),aFc)?a.toLocaleLowerCase():a.toLowerCase()}function Pic(a,b){var c;c=(n8b(),new Wbc((Afc(),rec),""));c.d=b;q8b(a.b,c);return c}function ZCc(a){var b;++a.a;for(b=a.c.a.length;a.aa)return new rJc;b=a+1;c=new iJc(b,a);return new oJc(c)}function dkc(){dkc=JZ;ckc=new ekc("ORDINARY",0);akc=new ekc(tRc,1);bkc=new ekc(yRc,2)}function jrc(a){var b;b=new hrc;b.p="Class$"+(a?"S"+a:""+b.k);b.b=b.p;b.n=b.p;return b}function Up(a,b){var c;c=pEc(a.a,b);if(!c){c=(wx(a.b.a),new uEc);qEc(a.a,b,c)}return c}function Rc(a,b){var c,d,e;c=false; +for(e=b.Vd();e.Id();){d=e.Jd();c=c|a.Pd(d)}return c}function KZ(a,b,c){var d=function(){return a.apply(d,arguments)};b.apply(d,c);return d}function yic(a,b,c){a.k=Qnc(a.k,Xkc(a.f,(Bn(),new Wz(Sd(b))),new Wz(Sd(c))));return a}function $_(a,b,c){var d;b0((Afc(),uec),b);for(d=b.c;d;d=d.f)c?j_(a,d):q0(a,d,false)}function A3b(a,b,c){Xd(H3b(a));Xd(H3b(b));Xd(H3b(c));return new O9b((Afc(),Sdc),a,b,c)}function Qe(a){Nd(true,"must be greater than zero: %s",2);return new Xe(a.c,a.b,a.d,2)}function AMb(a,b){BMb(a, +"msg.bad.fileoverview.visibility.annotation",b,a.j.g,lOb(a.j))}function l4b(a,b){!a.d&&(a.d=new L5b);!a.d.s&&(a.d.s=new fxc);Ywc(a.d.s,b);return true}function i5b(a,b){!a.d&&(a.d=new L5b);if(a.d.c!=null)return false;a.d.c=b;return true}function mqb(a){ppb();var b;if(a.n!=(Afc(),Jdc))return false;b=a.g;return!!b&&nqb(b)}function Jf(a,b,c){var d,e;d=(e=a.f,!e?a.f=new Wg(a,a.c):e).Ie(b);return!!d&&d.Sd(c)}function Ms(a,b,c){var d;el(b,c);d=pEc(a.a,b);!d&&qEc(a.a,b,d=new fxc);d.Pd(c);return a}function Kbb(a, +b){var c;for(c=0;c>>b%31&1)==1}function LCc(a){var b,c;c=(b=erc(fe(a)),new TCc(b,nKc(b,b.length),0));NCc(c,a);return c}function Dm(a){var b,c,d,e;for(c=a,d=0,e=c.length;d=48&&a<=49)return a-48;throw gZ(new Irc(QRc+String.fromCharCode(a)));}function PKb(a){if(a>=48&&a<=55)return a-48;throw gZ(new Irc(QRc+String.fromCharCode(a)));}function _qb(a){ppb();var b;b=$qb(a);if(b==null)throw gZ(new Hqc("Unknown op "+a));return b}function skb(a,b){var c,d,e;e=a;for(c=a,d=c.g;d!=b;c=d,d=c.g)ukb(d)&&c!=d.c&&(e=d);return e}function vlb(a,b){var c, +d,e;e=H6(a.a.a);d=e.Dg(b);if(d)return true;c=e.Eg(b);return c!=null}function wGc(a,b){var c,d;c=1-b;d=a.a[c];a.a[c]=d.a[b];d.a[b]=a;a.b=true;d.b=false;return d}function zmb(a,b,c){var d;d=new stc;d.a+="@";d.a+=b;d.a+=" {";ymb(a,d,c);d.a+="}";return d.a}function ubb(a,b,c){var d;d=b.vh()-c.vh();if(d!=0)return d;return Xtc(a.a,c).a-Xtc(a.a,b).a}function v3b(a,b,c){Xd(a.n==(Afc(),rec));Xd(b.n==Jec);Xd(c.n==Vcc);return new O9b(Jdc,a,b,c)}function D7b(a){var b;b=i4b(a,false);b.j==(e6b(),_5b)&&(b.j=null); +return new B7b(b,a.c,true)}function vUb(a){var b;b=(Bn(),new Zr);while(!AVb(a,(AZb(),XXb)))Xr(b,dVb(a));return In(b.a)}function rz(a){var b,c,d;b=0;for(d=a.Vd();d.Id();){c=d.Jd();b+=c!=null?Db(c):0;b=~~b}return b}function C_(a){var b,c;b0((Afc(),Bdc),a);c=C8b(a);2!=c&&D0(PMc+c,a);for(b=a.c;b;b=b.f)W_(b)}function L_(a){var b,c;b0((Afc(),Wdc),a);c=C8b(a);2!=c&&D0(PMc+c,a);for(b=a.c;b;b=b.f)W_(b)}function EB(a,b){var c,d,e;e=b.length;c=zE(kW,mLc,143,e,0,1);for(d=0;d2)&&D0(VMc+c,a);for(b=a.c;b;b=b.f)s0(b)}function xNb(a){while(OMb(a,(fOb(),TNb))){!a.o? +mOb(a.j):GMb(a);OMb(a,dOb)&&(!a.o?mOb(a.j):GMb(a))}}function iib(a,b,c,d){eib(a,aib(a,d,"yieldAll",CE(xE(HU,1),IMc,7,0,[b,mib(c,d)])));a.r.a.j.e=false}function oFb(a,b,c,d,e){var f;f=a.b.c.Lh(a.a,b,c,d,e);if(f!=null)return new qFb(a.b,f);return null}function bnb(a){var b,c,d,e,f;b=0;for(d=a.a,e=0,f=d.length;e0){c.a+=":";c.a+=b}c.a+=": "}return c.a}function nMb(a,b,c){var d,e,f;d=dKb(a.a,b);for(f=c.Vd();f.Id();){e=f.Jd();q8b(d,yKb(a.a,e))}return d}function vQb(a,b){var c,d;d=a.Ud()?(c=erc(FR),new TCc(c,nKc(c,c.length),0)):KCc(a);NCc(d,b);return d}function B4(a,b){var c;L3(a.c,"{");for(c=b.c;c;c=c.f){c!=b.c&&a.c.Yg();a.fh(c,(_4(),W4))}L3(a.c,"}")}function tz(a,b){Sd(b);pF(b,182)&&(b=Yi(b));return pF(b,35)&&b.Yd()>a.Yd()? +uu(a.Vd(),b):uz(a,b.Vd())}function CTb(a){if(AVb(a,(AZb(),oYb)))return CUb(a);if(AVb(a,bYb))return hUb(a,true);return ATb(a)}function TUb(a){var b;if(zVb(a,0,(AZb(),GYb))){iTb(a,GYb);b=nVb(a);iTb(a,LXb)}else b=aVb(a);return b}function sVb(a){var b,c;b=DWb(a.g,0).d.b;c=qVb(a);if(!AVb(a,(AZb(),EYb)))return c;return pVb(a,b,c)}function sUb(a,b,c){lwc(a.e,c);M$b(R$b(L$b(O$b(N$b(b,c.b),tTb(a)),pUb(a,0)),sTb(a)),qUb(a));wwc(a.e)}function y$b(a,b,c,d,e,f){IZb.call(this,(n1b(),d0b),a);this.d=b;this.e=c;this.a= +d;this.b=e;this.c=f}function YZb(a,b,c,d,e,f){IZb.call(this,(n1b(),M_b),a);this.d=b;this.b=c;this.e=d;this.c=e;this.a=f}function m_b(a,b,c,d,e,f){IZb.call(this,(n1b(),A0b),a);this.e=b;this.d=c;this.c=d;this.a=e;this.b=f}function gD(a,b,c,d,e,f,g,h){this.i=a;this.c=b;this.b=c;this.d=d;this.g=e;this.e=f;this.f=g;this.a=h}function K9b(a,b,c){n8b();this.n=a;this.g=null;this.k=b<0||c<0?-1:(c&-4096)!=0?b<<12|xMc:b<<12|c&xMc}function M9b(a,b,c,d){n8b();L9b.call(this,a,b);this.k=c<0||d<0?-1:(d&-4096)!=0? +c<<12|xMc:c<<12|d&xMc}function SE(a,b){var c,d,e;c=a.l+b.l;d=a.m+b.m+(c>>22);e=a.h+b.h+(d>>22);return HE(c&sMc,d&sMc,e&tMc)}function _E(a,b){var c,d,e;c=a.l-b.l;d=a.m-b.m+(c>>22);e=a.h-b.h+(d>>22);return HE(c&sMc,d&sMc,e&tMc)}function Vc(a,b){var c,d,e;c=false;for(d=a.Vd();d.Id();){e=d.Jd();if(b.Sd(e)){d.Kd();c=true}}return c}function _kb(a,b){var c,d;for(d=new wxc(N4b(b));d.a0)return $wc(a.n,b-1);else{c=twc(a.o); +return c?c.d:null}}function q5b(a,b){var c;!a.d&&(a.d=new L5b);if(a.d.n)return false;a.d.n=(bn(),c=b,mp(c));return true}function q4b(a,b){a.a|=4096;if(!d5b(a))return true;if(a.b.c!=null)return false;a.b.c=b;return true}function f4b(a){var b;if(!d5b(a))return null;!a.b.d&&(a.b.d=new fxc);b=new Q5b;Ywc(a.b.d,b);return b}function j3b(a){var b;ae(I3b(a),"Block node cannot contain %s",a.n);b=new L9b((Afc(),Vcc),a);return b}function _qc(a){var b;if(a<128){b=(brc(),arc)[a];!b&&(b=arc[a]=new Sqc(a));return b}return new Sqc(a)} +function dhc(a){var b,c;if(a.a)for(b=a.a.c;b;b=b.f){c=b.d;if(!!c&&Wfc(c))return true}return false}function nwc(a,b){if(b==null)return false;while(a.a!=a.b)if(zb(b,Pwc(a)))return true;return false}function vuc(a){if(a.a.Id())return true;if(a.a!=a.d)return false;a.a=new KDc(a.e.d);return a.a.Id()}function Zwc(a,b){var c,d;c=b._d();d=c.length;if(d==0)return false;pKc(a.a,a.a.length,c);return true}function ac(a,b){var c;for(c=b.length-1;c>=0;c--)if(!Bc(a,b.charCodeAt(c)))return false;return true}function Ohb(a){var b, +c;for(c=new Rwc(a.e);c.a!=c.b;){b=Pwc(c);if(b.b==0)return b.a;break}return null}function dnb(a){var b,c,d,e,f;f=new fDc;for(c=a.a,d=0,e=c.length;d=0);Xd(c>=0);a.f=new _B(b,c);e=new iC;f=qD(d);eC(e,f);hC(e,new TC(a))}function Wtc(a,b,c){var d,e;for(e=c.Vd();e.Id();){d=e.Jd();if(a.rj(b,d.We()))return true}return false}function Eyc(a){Cyc();var b,c,d;d=0;for(c=a.Vd();c.Id();){b=c.Jd();d=d+(b!=null?Db(b):0);d=d|0}return d}function KCc(a){var b, +c,d,e;!a.Ud();d=a.Vd();c=d.Jd();e=LCc(c);while(d.Id()){b=d.Jd();NCc(e,b)}return e}function CE(a,b,c,d,e){e.Fj=a;e.Gj=b;e.Hj=OZ;e.__elementTypeId$=c;e.__elementTypeCategory$=d;return e}function KMb(a,b,c,d){if(b==(fOb(),RNb)||b==TNb||b==SNb)return new FNb("",b);return JMb(a,b,c,true,d)}function jVb(a){var b,c;c=DWb(a.g,0).d.b;iTb(a,(AZb(),aZb));b=KTb(a,1);return new w1b(new Z1b(c,a.f),b)}function A_(a){var b,c;b0((Afc(),vdc),a);for(b=a.c;b;b=b.f){b0(cfc,b);g0(b);c=C8b(b);0!=c&&D0(OMc+c,b)}}function Te(a, +b){var c,d;Sd(b);c=a.c.je(a,b);d=new fxc;while(Pb(c))Ywc(d,Qb(c));return Cyc(),new gBc(d)}function Ug(a,b){var c,d;c=a.a.Ke(b);if(!c)return null;d=a.b.ye();d.Qd(c);a.b.d-=c.Yd();c.Rd();return d}function lmb(a,b){hmb();var c;if(H6(a).Ig(b))return true;c=b.c;return c.n==(Afc(),rec)&&gmb.Sd(c.Wh())}function Xqb(a,b,c){ppb();var d;d=Wqb(a,b);drb(d,c);Esc(DMc,H8b(d,(Hbc(),sbc)))||d9b(d,sbc,DMc);return d}function xqb(a){ppb();switch(a.n.f){case 29:case 26:case 91:case 27:return yqb(a);default:return false}} +function VSb(a){OSb();switch(a.f){case 109:case 108:case 110:case 111:return true;default:return false}}function X8b(a){switch(a.n.f){case 29:case 26:case 27:case 93:case 94:return true;default:return false}}function Qrc(a){var b;if(a<0)return hMc;else if(a==0)return 0;else{for(b=$Lc;(b&a)==0;b>>=1);return b}}function XE(a){var b,c,d;b=~a.l+1&sMc;c=~a.m+(b==0?1:0)&sMc;d=~a.h+(b==0&&c==0?1:0)&tMc;return HE(b,c,d)}function NE(a){var b,c,d;b=~a.l+1&sMc;c=~a.m+(b==0?1:0)&sMc;d=~a.h+(b==0&&c==0?1:0)&tMc; +a.l=b;a.m=c;a.h=d}function OE(a){var b,c;c=Trc(a.h);if(c==32){b=Trc(a.m);return b==32?Trc(a.l)+32:b+20-10}else return c-12}function Z2b(a){X2b();var b;b=Y2b(W2b,a);if(b!=null)return b;throw gZ(new GD("Resource not found: "+a));}function Wgb(){Wgb=JZ;Ugb=new Xgb("REWRITE_ALL_OBJECT_PATTERNS",0);Vgb=new Xgb("REWRITE_OBJECT_REST",1)}function kvb(){kvb=JZ;jvb=new Mcb("JSC_JSON_UNEXPECTED_TOKEN",(E2(),B2),new Rtc("Unexpected JSON token"))}function P2(){P2=JZ;O2=new Mcb("JSC_MISSING_PROVIDE",(E2(),D2), +new Rtc("missing goog.provide(''{0}'')"))}function t1(a,b){return DJc(FJc(EJc(new KJc(null,new BFc(a.c)),new Fwb(b)),new Hwb),(Bn(),Nk(),Nk(),Mk))}function eq(a,b,c){var d,e;return!b||c==null?null:(e=Bx((d=a.c,!d?a.c=new DA(a):d),b),!e?null:Bx(e,c))}function yMb(a,b,c){var d;a.c.Bh("Missing type declaration.",(d=H8b(a.n,(Hbc(),ybc)),!d?null:d.ug()),b,c)}function Ejb(a,b,c){var d,e;for(e=new wxc(Bpb(c));e.a>24);d!=a.j&&(a.j=d);c!=0&&(a.j=new oac(b.f<<24>>24,c,a.j))}function XUb(a,b){var c,d;d=DWb(a.g,0).d.b;iTb(a,(AZb(),aZb));c=cVb(a,b);return new PZb(new Z1b(d,a.f),c)}function NUb(a){var b;b=(Bn(),new Zr);while(!AVb(a,(AZb(),KXb))&&!AVb(a,XXb))Xr(b,dVb(a));return In(b.a)}function XJb(a,b){var c;if(b){c=TJb(a,b);jDc(a.k,b);if(!ZJb(a,c))return pKb(a,b.a,k6b(c.f))}return null}function qGc(a,b,c){var d,e;d=new XGc(b,c);e=new YGc;a.b=oGc(a,a.b,d,e);e.b||++a.c;a.b.b=false;return e.d}function ZDc(a,b, +c){var d;d=a.a.get(b);a.a.set(b,c===undefined?null:c);d===undefined?++a.b:++a.c;return d}function Dyc(a,b){Cyc();var c,d,e,f,g;g=false;for(d=b,e=0,f=d.length;e>24);d!=a.j&&(a.j=d);c!=null&&(a.j=new yac(b.f<<24>>24,c,a.j))}function STb(a){var b,c;c=DWb(a.g,0).d.b;iTb(a,(AZb(),FYb));b=iVb(a); +iTb(a,KXb);return new SZb(new Z1b(c,a.f),b)}function qUb(a){var b,c;c=DWb(a.g,0).d.b;iTb(a,(AZb(),FYb));b=iVb(a);iTb(a,KXb);return new SZb(new Z1b(c,a.f),b)}function lTb(a){var b;b=DWb(a.g,0);switch(b.e.f){case 101:case 100:return wTb(a);case 2:default:return kTb(a)}}function wD(b,c){uD();var d;try{vD(c,b)}catch(a){a=fZ(a);if(pF(a,104)){d=a;throw gZ(new ID(d));}else throw gZ(a);}}function Zkc(a,b){var c,d,e,f,g;c=new $pc(a,false);for(e=b,f=0,g=e.length;f127)return false}return true}function CDc(a,b,c){var d,e,f,g;for(e=c,f=0,g=e.length;f=d||b>24);d!=a.j&&(a.j=d);(c?1:0)!=0&&(a.j=new oac(b.f<<24>>24,c?1:0,a.j))}function Vfc(a){var b,c,d;b=(bn(),new Rs);for(d=a.B.c.Vd();d.Id();){c=d.Jd();Ywc(b.b,Sd(c))}return np(new wxc(b.b))}function jlc(a, +b,c){var d;mlc(c)&&(c=(!a.C&&(a.C=(d=new Vic(a),d.s=true,d)),a.C));Vf(a.D,b,c);Lkc(a,b,c);buc(a.j,b)}function Pwc(a){var b;HKc(a.a!=a.b);b=a.d.a[a.a];Gwc(a.b==a.d.c&&b!=null);a.c=a.a;a.a=a.a+1&a.d.a.length-1;return b}function nu(a,b){var c;Sd(a);Ld(b>=0,"numberToAdvance must be nonnegative");for(c=0;c=0;c--)if(a.c.ce(c)==b)return c;return-1}function Yrc(a){var b,c;if(a>-129&&a<128){b=a+128;c=($rc(),Zrc)[b];!c&&(c=Zrc[b]=new Krc(a));return c}return new Krc(a)}function Z8b(a){var b,c;c9b(a,(Hbc(),pbc),1);for(c=(!a.c?(Cyc(),Cyc(),Byc):new Kbc(a.c)).Vd();c.Id();){b=c.Jd();Z8b(b)}}function CMb(a,b,c,d){var e;a.c.Bh(URc+occ(b,CE(xE(iW,1),mLc, +1,5,[]))+VRc,(e=H8b(a.n,(Hbc(),ybc)),!e?null:e.ug()),c,d)}function tp(a,b,c,d,e,f,g){bn();var h;h=new gxc;Dyc(h,CE(xE(iW,1),mLc,1,5,[a,b,c,d,e,f]));Dyc(h,g);return np(new wxc(h))}function $Vb(a,b,c,d){d=Ysc(d);Esc(d.substr(0,21),"//# sourceMappingURL=")&&(a.b.i=d.substr(21));Xr(a.a,new $Zb(d,c,b))}function Qxc(a,b,c,d){var e,f,g;for(e=b+1;eb&&d.lf(a[f-1],a[f])>0;--f){g=a[f];a[f]=a[f-1];a[f-1]=g}}function Oxc(a){var b,c,d,e,f;if(a==null)return 0;f=1;for(c=a,d=0,e=c.length;dc&&(b[c]=null);return b}function Lfc(a,b){var c,d,e;if(Vt(b))return a;c=new kcc((Afc(),Iec),a);for(e=b.Vd();e.Id();){d=e.Jd();q8b(c,d)}return c}function Lc(b,c,d){var e;try{Kc(b,c,d)}catch(a){a=fZ(a);if(pF(a,104)){e=a;throw gZ(new Kqc(e));}else throw gZ(a);}return c} +function O2b(a){if(a.b==116||a.b==84)throw gZ(new Q2b(nF(a.b)+""+nF(a.c)));throw gZ(new Q2b(String.fromCharCode(a.b)));}function mJc(a){if(a.c)mJc(a.c);else if(a.d)throw gZ(new Irc("Stream already terminated, can't be modified or used"));}function Grc(a,b){if(ab)return 1;if(a==b)return a==0?Grc(1/a,1/b):0;return isNaN(a)?isNaN(b)?0:1:-1}function aWb(a,b){this.d=a==(iWb(),gWb);this.a=!(a==bWb||a==cWb);this.b=a==eWb||a==fWb||a==hWb;this.c=b;this.e=a==bWb}function kmb(a){this.g=hy(Yx((fl(8, +lQc),new $x)));this.e=new Fr;this.f=fy(Xx((fl(8,lQc),new $x)));this.d=new Cwc;this.c=a}function fXb(a,b,c,d,e){this.d=new fxc;this.g=a;this.e=b;this.a=c;this.i=d;this.b=d.a;this.c=d.a.length;this.f=e;this.j=0}function r8b(a,b){var c;Kd(!b.g);Kd(!b.f);Kd(!b.i);b.g=a;b.f=a.c;if(!a.c)b.i=b;else{c=a.c.i;b.i=c;b.f=a.c;a.c.i=b}a.c=b}function ZB(a,b){var c;b=b<0?(-b<<1)+1:b<<1;do{c=b&31;b>>>=5;b>0&&(c|=32);htc(a,(XB(),bMc,bMc.charCodeAt(c)))}while(b>0)}function Wkc(a,b){var c,d,e,f;f=b;c=new xnc(a);for(e= +po(f).Vd();e.Id();){d=e.Jd();vnc(c,d.Ve(),d.We(),null)}return wnc(c)}function Vnc(a){var b,c,d,e;if(a.b!=null)for(c=a.b,d=0,e=c.length;d>>0,b.toString(16));return a.toString()}function wnc(a){var b;if(a.b)return a.d.u[38];b=(ht(),new qt((Wy(),Vy)));pt(b,new puc(a.c));return new onc(a.d,ot(b),a.a)}function Xeb(a,b){var c,d;c9b(b,(Hbc(),pbc),1);for(d=(!b.c?(Cyc(),Cyc(),Byc):new Kbc(b.c)).Vd();d.Id();){c=d.Jd();Xeb(a,c)}}function cMb(a,b){var c,d,e;e=eKb(a.a,(Afc(),gfc),yKb(a.a,b.b)); +for(d=b.a.Vd();d.Id();){c=d.Jd();q8b(e,yKb(a.a,c))}return e}function AKb(a,b){var c,d;c=VJb(a,b.o);d=iLb(a.t,b);!!c&&(d9b(d,(Hbc(),mbc),c),d);T9b(d.k)==-1&&vKb(d,b.o.b,b.o.a);return d}function Whb(a,b){var c,d;for(d=new wxc(b);d.a=0;c--)if(Zsc(b,c)!=d)break;return ctc(b,0,c+1)}function Tib(a,b,c){var d;Xd(c.n==(Afc(),rec));d=i$(b,c.Wh());if(!!d&&!!d.c&&uqb(d.c)&&c!=d.c)return Qib(a,d.c);return null}function lLb(a,b){var c;bKb(a.a,b,(IRb(),FQb));c=eKb(a.a,(Afc(),Sec),AKb(a.a,b.a));T9b(c.k)==-1&&vKb(c,b.o.b,b.o.a);return c}function eLb(a,b){if(Efc(b.Wh())){a.a.e=oQb(a.a.e,(IRb(),eRb));a.a.a.d==(zJb(),pJb)&&a.a.d.Bh(YQc,a.a.q,T9b(b.k),S9b(b.k))}}function GUb(a){var b,c;c=DWb(a.g,0).d.b;b=wTb(a);b.e==(AZb(),gZb)&&b.a&&RVb(a, +(IRb(),HRb));return new j_b(new Z1b(c,a.f),b)}function Sbb(){Sbb=JZ;Obb=new Tbb(qOc,0);Rbb=new Tbb("SORT_ONLY",1);Qbb=new Tbb("PRUNE_LEGACY",2);Pbb=new Tbb("PRUNE",3)}function e6b(){e6b=JZ;b6b=new f6b(ARc,0);a6b=new f6b(zRc,1);c6b=new f6b(BRc,2);d6b=new f6b(CRc,3);_5b=new f6b("INHERITED",4)}function pgb(){pgb=JZ;ogb=new qgb("QUOTED_PROPERTY",0);mgb=new qgb("COMPUTED_PROPERTY",1);ngb=new qgb("NORMAL_PROPERTY",2)}function K0(a,b,c,d,e,f,g,h,i,j){this.i=a;this.f=b;this.e=c;this.j=d;this.b=e;this.g=f; +this.d=g;this.a=h;this.c=i;this.k=j}function yob(a,b,c,d,e,f){this.b=new fDc;this.a=a;this.f=b;Kd(c.length!=0);this.d=c;this.e=d;e?this.c=this:this.c=f.Eh()}function F8(a,b,c){this.f=new fxc;this.e=new fxc;this.o=new fxc;this.d=new fxc;this.j=(b9(),a9);this.a=a;this.i=b;c&&C8(this)}function dpb(a,b,c){this.o=new Cwc;this.n=new fxc;this.b=new Cwc;this.a=b;pF(b,317)&&(this.j=b);this.c=a;this.k=c;this.q=true}function wwb(a){uwb.call(this,a);Ld(zpb(this.d)||this.d.n==(Afc(),Xec)||this.d.n==(Afc(),Vec), +this.d);this.b=null;this.a=0}function xtc(a,b,c){var d,e;Bb(a);Bb(b);e=a.length;d=b.length;if(c<0||c>e||c>d)throw gZ(new Eqc);c>0&&mKc(a,0,b,0,c,true)}function CLb(a,b){var c,d,e;e=eKb(a.a,(Afc(),Ycc),yKb(a.a,b.b));for(d=b.a.a.Vd();d.Id();){c=d.Jd();q8b(e,yKb(a.a,c))}return e}function M3b(a,b){var c,d,e,f,g;g=new L9b((Afc(),xec),a);for(d=b,e=0,f=d.length;e=48&&c<=57){M2b(b,B2b(a));return D2b(a,b)}else throw gZ(G2b(a));}function Zub(a,b){Sd(a);Sd(b);Kd(b.n==(Afc(),Jdc));this.a=b;b.n==Jdc&&E8b(b,(Hbc(),Fac))!=0?this.b= +a.b:this.b=new $ub(this)}function V8(a,b){var c,d;U8(a,b,null);Kd(b.n==(Afc(),Xec));c=H8b(b,(n8b(),V7b));if(c){d=nQb(c);Esc(d,"es3")||qGc(a.a,"lang",d)}}function i$(a,b){var c,d,e;c=b!=null?s$(b):null;if(c)return g$(a,c);d=a;while(d){e=pEc(d.e,b);if(e)return e;d=d.b}return null}function Ipb(a){ppb();var b,c;Ld(a.n==(Afc(),bdc),a);b=Td(a.c?a.c.i:null,a);for(c=b.c;c;c=c.f)if(nqb(c))return c;return null}function Mqb(a){ppb();switch(a.f){case 24:case 79:case 25:case 21:case 22:case 20:case 19:return true; +default:return false}}function r7b(a,b){if(!!b&&!((a.a.a&HPc)==WPc||(a.a.a&HPc)==hMc||(a.a.a&HPc)==eQc)){l4b(a.a,b);a.e=true;return true}return false}function glb(a,b){return a.e||(!!a.c&&epb(a.a,a.c,new zlb(a)),epb(a.a,a.i,new zlb(a)),a.e=true,a.d=null,undefined),Ytc(a.g,b)}function j4b(a,b,c){!a.d&&(a.d=new L5b);!a.d.o&&(a.d.o=new tEc);if(oEc(a.d.o,c))return false;else{qEc(a.d.o,c,b);return true}}function iVb(a){var b;b=(Bn(),new Zr);while(zVb(a,0,(AZb(),gYb))||NVb(a)||AVb(a,sYb)||zVb(a,0,IXb))Xr(b, +hVb(a));return In(b.a)}function kVb(a){var b;b=(Bn(),new Zr);while(zVb(a,0,(AZb(),gYb))||NVb(a)||AVb(a,sYb)||zVb(a,0,IXb))Xr(b,hVb(a));return In(b.a)}function S_(a){var b,c;b0((Afc(),fec),a);c0(a)&&a.Wh().length==0&&D0(WMc,a);c=Cfc(a.n);c!=-1&&(b=C8b(a),c!=b&&D0(QMc+c+RMc+b,a))}function W_(a){var b,c;b0((Afc(),rec),a);c0(a)&&a.Wh().length==0&&D0(WMc,a);c=Cfc(a.n);c!=-1&&(b=C8b(a),c!=b&&D0(QMc+c+RMc+b,a))}function ryc(a,b){var c,d;if(a==b)return;yyc(a.a,b.a.length-1);for(c=0;ca.g?a.i.u[51]:b>1?null:b==1?a.a.a[0]:a.i.u[58]}function mj(a){var b,c,d,e;for(d=(e=(new fvc(a.a)).a.He().Vd(),new kvc(e));d.a.Id();){c=(b=d.a.Jd(),b.We());c.a=0}Ttc(a.a);a.b=0}function ydb(a){var b,c;Ld(a.n==(Afc(),Jdc),a);c=H8b(a,(Hbc(),Dbc));b=c?c:a.d;if(!b)return null;Xd(!!b.Ii());return Cjc(b.Ii())}function Qn(a){Bn();var b,c,d;for(c=0,d=a.length;cc)throw gZ(new Fqc(STc+a+TTc+b+", size: "+c));if(a>b)throw gZ(new qnb(STc+a+" > toIndex: "+b));}function F1(a,b){if(E8b(b,(Hbc(),Pac))!=0)throw gZ(new Irc(a+"existing scope is improperly marked as deleted:\n"+E9b(b)));}function KVb(a,b){var c;c=DWb(a.g,b).e;switch(c.f){case 2:case 101:case 100:return true;default:return OSb(), +OSb(),!!Po(pSb,c)}}function OVb(a){switch(DWb(a.g,0).e.f){case 9:case 26:case 24:case 63:case 64:case 77:case 76:return true;default:return false}}function Y$b(){Y$b=JZ;V$b=new Z$b("DECLARATION",0);W$b=new Z$b("EXPRESSION",1);X$b=new Z$b("MEMBER",2);U$b=new Z$b("ARROW",3)}function PC(){this.e=new fxc;this.k=new tEc;this.j=new tEc;this.g=new tEc;this.f=new _B(0,0);this.i=new _B(0,0);this.a=new tEc}function VEb(a){this.f=new fxc;this.c=new fxc;this.a=new mDc;this.e=new tEc;this.b=new fDc;this.d=new hv; +Zwc(this.f,a);UEb(this)}function Uwb(a,b){if(!a)throw gZ(new qnb("a source must have a name"));Esc("/",tqc)?this.b=a:this.b=Osc(a,tqc,"/");this.c=b}function Db(a){return tF(a)?TKc(a):rF(a)?vF(a):qF(a)?a?1231:1237:oF(a)?a.Ad():BE(a)?NKc(a):!!a&&!!a.hashCode?a.hashCode():NKc(a)}function z4(a,b,c,d,e){var f,g;for(g=b;g;g=g.f){f=g==b;if(f)v4(a,g,c?1:0,d);else{N3(a.c,e,true);v4(a,g,c?1:0,d.b?d:(_4(),W4))}}}function dac(a,b,c){var d,e;for(e=0;e!=b;++e)c.a+=" ";ltc(c,D9b(a,true,true,true));c.a+=gMc;for(d= +a.c;d;d=d.f)dac(d,b+1,c)}function Sf(a){var b,c,d,e;for(c=(e=(new fvc(a.c)).a.He().Vd(),new kvc(e));c.a.Id();){b=(d=c.a.Jd(),d.We());b.Rd()}a.c.Rd();a.d=0}function k3b(a){var b,c,d,e,f,g;c=(b=new J9b((Afc(),Vcc)),b);for(e=a,f=0,g=e.length;f0){$wnd.Error.stackTraceLimit=Error.stackTraceLimit=64;return true}return fMc in +new Error}function H1(a){Xd(a.n==(Afc(),Vec));if(E8b(a,(Hbc(),Hac))!=0)throw gZ(new Irc("Root nodes should never be marked as changed."));}function TKc(a){RKc();var b,c,d;c=":"+a;d=QKc[c];if(d!=null)return vF(d);d=OKc[c];b=d==null?SKc(a):vF(d);UKc();QKc[c]=b;return b}function f3b(a){var b,c,d,e,f;b=new J9b((Afc(),zcc));for(d=a,e=0,f=d.length;e0&&(d.k=Xkc(d.f,(Bn(),Sd(c),In(new eyc(c))),null),d);return uic(d)}function Gjc(a,b,c){if(!b)return false;if(a.oi()&&b==(ae(a.oi()||a.f==(dkc(),bkc),lRc,a),cgc(a.k)))return false;return Ijc(a,b,c)}function hPb(a,b){if(b.n!=(Afc(),rec)){a.a.Bh(URc+occ(jSc,CE(xE(iW, +1),mLc,1,5,["name"])),a.b.ug(),a.d,a.c);return false}return true}function r3b(a,b){Yd(a.n==(Afc(),rec)||a.n==Fec||a.n==Acc||a.n==odc,a);(a.n==Fec||a.n==Acc)&&(a=new L9b(odc,a));return new L9b(b,a)}function K4(a,b,c,d,e,f,g,h){var i,j;j=b.c;while(j.n==c)j=j.c;v4(a,j,g,e);i=j;do{i=i.g;N3(a.c,d,true);v4(a,i.c.f,h,f)}while(i!=b)}function QC(a,b){var c,d,e,f;e=0;for(d=new TEc(new KEc(b));d.b!=d.c.a.b;){c=REc(d);f=c.d;e!=0&&(a.a+=",",a);ltc(a,(uD(),xD(f)));++e}}function Wp(a){var b,c,d,e,f;f=0;for(d=(e= +(new fvc(a.a)).a.He().Vd(),new kvc(e));d.a.Id();){c=(b=d.a.Jd(),b.We());f+=c.Yd()}return f}function Gfb(a,b,c,d){var e,f;f=z3b(Ffb(a,b),d,CE(xE(nW,1),uNc,2,6,[]));if(a.f.d){e=c.d;!e&&(e=pfb(a.f,51));f.d=e}G9b(f,c);return f}function Qy(a,b,c,d){var e;fl(c,"oldCount");fl(d,"newCount");e=Bx(a.a,b);if((!e?0:e.a)==c){sj(a,b,d);return true}else return false}function hMb(a,b){var c,d;d=b.a.uf();c=iKb(a.a,(Afc(),rec),d.Jd());while(d.Id())c=x3b(c,V3b(d.Jd()));return SJb(a.a,new kcc(pfc,c))}function oVb(a){var b, +c,d;d=DWb(a.g,0).d.b;iTb(a,(AZb(),rZb));b=jTb(a);iTb(a,ZXb);c=nVb(a);nTb(a);return new F1b(new Z1b(d,a.f),b,c)}function fUb(a,b){var c,d,e,f;f=DWb(a.g,0).d.b;c=bVb(a,b);while(EVb(a)){d=wTb(a);e=bVb(a,b);c=new RZb(new Z1b(f,a.f),c,d,e)}return c}function Qob(a,b){var c,d,e;c=Fpb(b);a.g=c;e=Lob(c);e?Wob(a,H8b(e,(Hbc(),cbc)),(d=H8b(e,ybc),!d?null:d.ug())):Wob(a,null,"");a.f=e}function Woc(a,b){Ofc();var c,d,e;dgc.call(this,a);this.b=b;e=new $pc(a,true);for(d=b.Vd();d.Id();){c=d.Jd();Upc(e,c)}this.a=Wpc(e)} +function Ypc(a,b,c){var d,e;e=!!b.Li()&&(b.Li(),true);d=!!c.Li()&&(c.Li(),true);return a.k&&!e&&!d?b.zi(c):b.Ai(c,new Akc(false),0)}function Lgb(a,b,c,d){var e;!!d&&d.n==(Afc(),odc)&&(d=d.g);switch(c.n.f){case 93:case 94:Ngb(a,b,c,d);c==rwc(a.e).b&&(e=wwc(a.e),e)}}function KGb(){KGb=JZ;JGb=new Mcb("JSC_USELESS_EMPTY_STATEMENT",(E2(),C2),new Rtc("Useless empty statement. (fix with go/fixjs)"))}function JNb(){JNb=JZ;HNb=new KNb("SEARCHING_ANNOTATION",0);INb=new KNb("SEARCHING_NEWLINE",1);GNb=new KNb("NEXT_IS_ANNOTATION", +2)}function Fsc(a,b){if(b==null)return false;if(Esc(a,b))return true;return a.length==b.length&&Esc(a.toLowerCase(),b.toLowerCase())}function M4(a,b){p4(this);this.c=a;this.g=b.sc;this.k=b.qd;this.i=b.vc;this.j=u9(b);this.n=false;this.f=l9(b);this.e=new Emb(this.n)}function Txb(){this.a=new _3b(hRc);this.c=new Uwb(hRc,(scc(),qcc));this.b=new J9b((Afc(),Xec));p9b(this.b,this.a);y9b(this.b,this.c)}function R3b(a){var b,c,d,e,f;f=new J9b((Afc(),Jec));for(c=a,d=0,e=c.length;d>1);g=a[f];if(gc)d=f-1;else return f}return-e-1}function Exc(a,b,c){var d,e,f,g;e=0;d=b-1;while(e<=d){f=e+(d-e>>1);g=a[f];if(gc)d=f-1;else return f}return-e-1}function M$(a,b,c){var d,e,f,g,h,i,j;i=(j=i3b((Afc(),edc),a,b),j);for(e=c,f=0,g=e.length;f0)return true;c=twc(a.d);if(!c.c)return false;while(!!c&&!!c.a){Vf(a.g,c.a,b);c=c.b}return true}function MLb(a,b){var c,d;c=VJb(a.a,b.d);fLb(a,b);d=iKb(a.a,(Afc(),rec),b.a);!!c&&(d9b(d,(Hbc(),mbc),c),d);vKb(d,b.d.b,b.d.a);return d}function PLb(a,b){var c,d,e;e=eKb(a.a,(Afc(),xec),yKb(a.a,b.b));if(b.a)for(d=b.a.a.Vd();d.Id();){c=d.Jd();q8b(e,yKb(a.a, +c))}return e}function NMb(a,b){var c,d;d=false;while(true){c=jOb(a.j);if(c==32)continue;else if(c==b){d=true;break}else break}pOb(a.j,c);return d}function lGc(a,b,c){var d,e,f;e=null;f=a.b;while(f){d=a.a.lf(b,f.d);if(c&&d==0)return f;if(d>=0)f=f.a[1];else{e=f;f=f.a[0]}}return e}function VD(){var a;if(QD!=0){a=PD();if(a-RD>2E3){RD=a;SD=$wnd.setTimeout(_D,10)}}if(QD++==0){cE((bE(),aE));return true}return false}function Kjb(a){var b;for(b=a.c;b;b=b.f){if(b.n!=(Afc(),jfc))continue;if(b.Th()==null||!Esc(b.Th(), +b.Vh()))return false}return true}function f7b(a,b,c){if(!((a.a.a&HPc)==WPc||(a.a.a&HPc)==hMc||(a.a.a&HPc)==eQc)&&j4b(a.a,c,b)){a.e=true;return true}else return false}function Zpb(a,b,c){var d;if(b.Ld(a))return true;if(!c.Ld(a))return false;for(d=a.c;d;d=d.f)if(Zpb(d,b,c))return true;return false}function svb(a,b,c){var d,e;for(e=(!c.c?(Cyc(),Cyc(),Byc):new Kbc(c.c)).Vd();e.Id();){d=e.Jd();if(d.n==(Afc(),_ec)){tvb(a,b,c);return}}}function vfb(a,b,c,d){jfb();var e;if(a.n==(Afc(),gdc)&&!(b.n==Hdc&&a== +b.c)){ufb(a,c,d);e=E7b(H8b(d,(Hbc(),mbc)));w6b(e);q9b(d,k6b(e))}}function z0(a,b){var c;ppb();if(!!b&&(b.n==(Afc(),ufc)||b.n==jec||b.n==gdc)){c=C8b(b);1!=c&&D0(UMc+c,b);Y_(a,b.n,b)}else R_(a,b.g.n,b)}function aKb(a,b,c){var d;a.e=oQb(a.e,c);lQb(a.a.d.a,c)||a.d.Bh((d=BJb(c),d==(zJb(),yJb)?LRc+c:MRc+BJb(c)+NRc+c),a.q,b.d.b.b+1,b.d.b.a)}function bKb(a,b,c){var d;a.e=oQb(a.e,c);lQb(a.a.d.a,c)||a.d.Bh((d=BJb(c),d==(zJb(),yJb)?LRc+c:MRc+BJb(c)+NRc+c),a.q,b.o.b.b+1,b.o.b.a)}function cKb(a,b,c){var d;a.e= +oQb(a.e,c);lQb(a.a.d.a,c)||a.d.Bh((d=BJb(c),d==(zJb(),yJb)?LRc+c:MRc+BJb(c)+NRc+c),a.q,T9b(b.k),S9b(b.k))}function UZ(a,b){switch(b.f){case 0:return VZ(a,zMc,(b$(),ZZ));case 1:return VZ(a,AMc,(b$(),a$));default:return b.e!=null?b.e:""+b.f}}function LVb(a,b){switch(DWb(a.g,0).e.f){case 54:case 55:case 57:case 56:case 17:return true;case 16:return b==1;default:return false}}function x6b(a){if(!((a.a.a&HPc)==WPc||(a.a.a&HPc)==hMc||(a.a.a&HPc)==eQc)&&!U4b(a.a)){a.a.a|=2;a.e=true;return true}else return false} +function y6(a){a.J=Ssb(B6(a),new ctb(a.K));Nsb(a.J,(!a.I&&(a.I=new CGb(a.a)),new fxc));Qsb(a.J,a.o,a.v);if(a.g.a.d!=0)return;a.J=null}function Onb(a){Knb();this.b=a;this.a=false;this.c=(!a.P&&(a.P=new llc),a.P);this.e=Wjb(this.a,this.c,51);this.d=Wjb(this.a,this.c,45)}function ggb(a){Yfb();var b;this.a=a;this.c=(!a.P&&(a.P=new llc),a.P);b=blc(this.c,"ObjectPropertyDescriptor");this.b=b?b:this.c.u[51]}function LCb(a){ICb();this.e=new YEc;this.a=a;this.c=J6(a,new Gmb("",null,0,0,DCb,null,CE(xE(nW,1), +uNc,2,6,[])))==(E2(),B2);this.d=true}function KHb(){KHb=JZ;JHb=new Mcb("JSC_MISSING_NAMESPACE_IMPORT",(E2(),B2),new Rtc('Imported Closure namespace "{0}" never defined.'))}function I1(a,b,c){var d,e;d=b.length==0?"":b+": ";e=new mDc;ppb();erb(qq(a.a,c),new L1(e),(se(),pe));erb(c,new N1(a,e,d),pe);D1(a,d,e)}function Roc(a,b){var c,d,e;d=new $pc(a.A,false);for(c=0;c0&&iZ(a,128)<0){b=yZ(a)+128;c=(ksc(),jsc)[b];!c&&(c=jsc[b]=new _rc(a));return c}return new _rc(a)}function $bb(a,b){Wbb();var c,d,e,f,g;g=(bn(),new Rs);for(d=b,e=0,f=d.length;e0)return A2b(a)}return yZ(b)}function Sic(a,b,c,d,e){var f,g;if((cnc(a.t,b)?1:0)!=0&&(g=a.Yi(b),!!g&&!g.b))return false;f=new Xmc(b,c,d,e);gnc(a.t,b,f);return true}function gtc(a,b,c){var d,e,f,g;f=b+c;KKc(b,f,a.length);g="";for(e=b;ea)throw gZ(new qnb("fromIndex: 0 > toIndex: "+a));if(a>b)throw gZ(new Gqc("fromIndex: 0, toIndex: "+ +a+RTc+b));}function Upb(a){ppb();var b;for(b=a;true;b=b.c){if(b.n==(Afc(),rec)||b.n==lfc||b.n==ffc)return b;ae(b.n==Pdc,"Not a getprop node: ",b)}}function RGb(){RGb=JZ;QGb=new Mcb("JSC_MUST_COME_BEFORE_IN_ES6_MODULE",(E2(),D2),new Rtc("In ES6 modules, {0} should come before {1}."))}function R5b(a,b){if(!a&&!b)return true;if(!a&&!!b||!!a&&!b)return false;return X5b(a.a,b.a)&&V5b(a.c,b.c)&&X5b(a.b,b.b)&&$5b(a.d,b.d)}function nnc(a,b,c,d){if(Igc(a,b,c,d))return true;if(a.A.u[38].Ai(b,c,d))return true; +if(!b.Ji())return false;return Ehc(a,b.Ji(),c,d)}function exc(a,b){var c,d;d=a.a.length;b.lengthd&&(b[d]=null);return b}function dyc(a,b){var c,d;d=a.a.length;b.lengthd&&(b[d]=null);return b}function SLb(a,b){var c,d,e,f;bKb(a.a,b,(IRb(),lRb));f=dKb(a.a,(Afc(),Fec));for(d=b.a.Vd();d.Id();){c=d.Jd();e=TLb(a,c);q8b(f,e)}return f}function H5b(a,b){var c,d,e;e=null;if(a){e= +new gxc;for(d=new wxc(a);d.a=f){zwc(a,b);return-1}else{Awc(a,b);return 1}}function i4b(a,b){var c;c=new A5b;c.d=!a.d?null:G5b(a.d,b);c.b=a.b;c.j=a.j;c.a=a.a;c.i=D5b(a.i,b);c.g=D5b(a.g,b);c.c=a.c;c.f=a.f;return c} +function Mjb(a,b,c){var d,e;d=Yjb(f3b(CE(xE(HU,1),IMc,7,0,[])),b);for(e=a.c;e;e=e.f)e.n==(Afc(),jfc)&&q8b(d,Yjb(V3b(e.Vh()),c));return d}function A0(a,b){var c;ppb();!!b&&(b.n==(Afc(),ufc)||b.n==jec||b.n==gdc)?Y_(a,b.n,b):b.n==(Afc(),tdc)?(c=C8b(b),0!=c&&D0(OMc+c,b)):E_(a,b)}function qpb(a,b){ppb();var c,d;Yd(a.n==(Afc(),Xec),a);c=(Yd(a.n==Xec,a),H8b(a,(n8b(),V7b)));d=c?oQb(c,b):oQb((iQb(),TPb),b);d9b(a,V7b,d)}function Heb(a,b,c){var d,e;for(e=c.Vd();e.Id();){d=e.Jd();d.n==(Afc(),bfc)&&Geb(a,b,d, +true);Heb(a,b,!d.c?(Cyc(),Cyc(),Byc):new Kbc(d.c))}}function LIc(a,b,c,d){(xIc?b.qj()>=(HIc(a),800):yIc?b.qj()>=(bIc(),800):BIc?b.qj()>=(bIc(),900):AIc&&b.qj()>=(bIc(),1E3))&&DIc(a,b,c,d)}function KIc(a,b,c){(xIc?b.qj()>=(HIc(a),800):yIc?b.qj()>=(bIc(),800):BIc?b.qj()>=(bIc(),900):AIc&&b.qj()>=(bIc(),1E3))&&DIc(a,b,c,null)}function X_(a,b,c){if(c.n==(Afc(),rec)){c0(c)&&c.Wh().length==0&&D0(WMc,c);T_(c);!!c.c&&E_(a,c.c)}else c.n==odc?y_(a,b,c):D0(SMc+b+TMc,c)}function L4(a){p4(this);this.c=a;this.g= +false;this.k=true;this.i=false;this.j=false;this.n=false;this.f=(iQb(),TPb);this.e=new Emb(false)}function Q4(b){var c;if(T4(b))try{c=Drc(b);if(qZ(c,{l:0,m:0,h:512}))return xZ(c)}catch(a){a=fZ(a);if(!pF(a,82))throw gZ(a);}return NaN}function fTb(a,b){var c;c=(Bn(),new Zr);Xr(c,b?b.a:"");while(AVb(a,(AZb(),MYb))){iTb(a,MYb);b=jTb(a);if(!b)break;Xr(c,b.a)}return In(c.a)}function ijb(a,b){var c,d,e;c=z3b(a,b,CE(xE(nW,1),uNc,2,6,[]));e=a.d;if(!e)return c;d=Tfc(e,b);!d&&pF(e,128)&&(d=yjc(e,b));return c.d= +d,c}function Jtb(a,b){Itb();var c;if(b==(NFb(),MFb)){c=C8b(a)>=3?B8b(a,2):a.c.f;return c.n==(Afc(),Cec)?""+vF(c.Uh()):c.Wh()}return a.c.f.Wh()}function zjc(a){var b;if(!a.g){b=a.Xi();b==null?Ijc(a,a.A.u[51],null):Gjc(a,new Wic(a.A,a.Xi()+fQc,a.A.u[38],a.q,null),null)}return a.g.e}function tKc(a,b){(!b&&console.groupCollapsed!=null?console.groupCollapsed:console.group!=null?console.group:console.log).call(console,a)}function E1(a,b,c){var d;d=E8b(c,(Hbc(),Hac));if(d==0||d=0;d=syc(b,d-1))e[d]+=1}return e}function Soc(a,b,c,d){var e,f,g;f=(Yoc(new Epc,a.b)&&Voc(a),a.b);for(g=0;g=0){++a.b;d=c}d==-1?a.a+=b.length:a.a=b.length-(d+1);return a}function O9(){O9=JZ;L9=new P9(qOc,0);M9=new P9("RELATIVIZE_IMPORT_PATHS",1);N9=new P9("TO_COMMON_JS_LIKE_MODULES",2);K9=new P9("COMPILE",3)}function Xab(){Xab=JZ;Sab=new Yab(COc,0);Vab=new Yab("RAW_SIZE",1);Tab=new Yab("AST_SIZE",2);Wab=new Yab("TIMING_ONLY",3);Uab=new Yab(GMc,4)}function Cgb(a,b){var c,d,e,f,g;g=Y$((d=vPc,d));c9b(g,(Hbc(),pbc),1);c=r9b(x8b(a,false),a.d);return f=A3b((e=i3b((Afc(), +Zec),a,g),e),b,c),f}function bjb(a,b,c){this.i=a;this.a=b;this.g=c;this.f=null;this.b=new xGc;this.c=new YEc;this.d=new mDc;this.e=z8(L6(b,H8b(c,(Hbc(),cbc))))}function XFb(){XFb=JZ;WFb=CE(xE(nW,1),uNc,2,6,["",fOc,".json",".i.js",".js.i.js"]);VFb=CE(xE(nW,1),uNc,2,6,[VQc,"/index.js","/index.json"])}function hhc(a,b,c,d){Ofc();dgc.call(this,a);this.a=!b?Vkc(a,true,CE(xE(cV,1),xPc,23,0,[this.A.u[51]])):b;this.b=!c?this.A.u[51]:c;this.c=d}function zb(a,b){return tF(a)?Esc(a,b):rF(a)?uF(a)===uF(b):qF(a)? +a===b:oF(a)?a.yd(b):BE(a)?wb(a,b):!!a&&!!a.equals?a.equals(b):uF(a)===uF(b)}function Opb(a){ppb();switch(a.n.f){case 72:case 73:case 101:case 102:case 70:return a.c?a.c.i:null;case 71:return a.c;default:return null}}function USb(a){OSb();switch(a.f){case 36:case 37:case 38:case 39:case 40:case 41:case 42:case 43:case 44:return true;default:return false}}function gXb(a){switch(a){case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:return true;default:return false}}function tsb(a){var b, +c,d;for(d=v8b(a.n).Vd();d.Id();){b=d.Jd();a.k+=1;a.j+=qsb(b)}for(c=v8b(a.u).Vd();c.Id();){b=c.Jd();a.v+=1;a.t+=qsb(b)}}function VLb(a,b){var c,d,e,f;e=(Bn(),new Zr);for(d=b.a.Vd();d.Id();){c=d.Jd();Xr(e,yKb(a.a,c))}f=yKb(a.a,b.b);return SJb(a.a,Lfc(f,In(e.a)))}function v2b(a,b){b.a=b.a.substr(0,0)+"("+b.a.substr(1);if(a.b.k){N2b(a.b,a.b.p-1);b=j2b(a,b,1);b.a+=")"}else{b.a+=")";b=j2b(a,b,0)}return b}function Vkc(a,b,c){var d,e,f;d=new Qic(a);f=c.length-1;for(e=0;e<=f;e++)b&&e==f?Oic(d,c[e]):Nic(d, +CE(xE(cV,1),xPc,23,0,[c[e]]));return d.b}function Zhb(a,b,c,d){var e,f;for(f=new wxc(b);f.a0?f=axc(a.n,e-1):f=(d=vwc(a.o),d).d;ppb();QCc(ipb,f.n)&&(c=vwc(a.b),c)}function HVb(a){var b;b=DWb(a.g,0);switch(b.e.f){case 18:case 2:case 49:case 65:case 54:case 47:return true; +default:return OSb(),!!QSb(b.e)}}function e$b(){e$b=JZ;_Zb=new f$b("BLOCK",0);c$b=new f$b("LINE",1);b$b=new f$b("JSDOC",2);d$b=new f$b("SHEBANG",3);a$b=new f$b("IMPORTANT",4)}function p7b(a,b){if(!!b&&!((a.a.a&HPc)==WPc||(a.a.a&HPc)==hMc||(a.a.a&HPc)==eQc)&&!a.a.g){w5b(a.a,b);a.e=true;return true}else return false}function jqb(a){ppb();var b;if(!!a&&(a.n==(Afc(),ufc)||a.n==jec||a.n==gdc))for(b=a.c;b;b=b.f)if(b.n==(Afc(),odc))return true;return false}function i0(a,b,c){var d,e;b0((Afc(),cfc),c);g0(c); +e=C8b(c);1!=e&&D0(UMc+e,c);d=c.c;switch(d.n.f){case 121:x_(a,b,d);break;default:R_(a,b,d)}}function N2(){N2=JZ;M2=new Mcb("JSC_MISSING_RETURN_STATEMENT",(E2(),D2),new Rtc("Missing return statement. Function expected to return {0}."))}function MRb(a){var b,c,d,e,f,g;g=(b=erc(FR),new TCc(b,nKc(b,b.length),0));for(d=LRb(),e=0,f=d.length;e=a.length)throw gZ(new e2b(Vsc(c.a,c.d,c.b)));if(b==-2)return d; +return a[b]}function Zqc(a,b,c){EKc(a>=0&&a<=lMc);if(a>=kMc){b[c++]=55296+(a-kMc>>10&mMc)&dLc;b[c]=56320+(a-kMc&mMc)&dLc;return 2}else{b[c]=a&dLc;return 1}}function bpb(a,b,c,d){Sd(c);Qob(a,b);if(Kob(a)!=d){a.e=b;Sd(a.e);kwc(a.o,d);Tob(a,d.d);!!a.j&&a.j.yh(a);Zob(a,b,c);Rob(a,false)}else Zob(a,b,c)}function drb(a,b){var c;if(!H8b(a,(Hbc(),ybc))){z9b(a,b);w9b(a,b.k)}H8b(a,sbc)==null&&d9b(a,(n8b(),g8b),H8b(b,sbc));for(c=a.c;c;c=c.f)drb(c,b)}function NDb(a,b){MDb();return $Db(ZDb(_Db(eEb(dEb(cEb(bEb(aEb(new fEb, +b),a),(Bn(),Bn(),An)),(null,An)),(null,An)),(oo(),oo(),no)),false),false)}function NFb(){NFb=JZ;JFb=new OFb("BROWSER",0);KFb=new OFb("BROWSER_WITH_TRANSFORMED_PREFIXES",1);LFb=new OFb("NODE",2);MFb=new OFb("WEBPACK",3)}function zKb(a,b){var c,d;c=yKb(a,b);if(c.n!=(Afc(),Vcc)){if(c.n==tdc)A9b(c,Vcc);else{d=eKb(a,Vcc,c);wKb(d,c);c=d}c9b(c,(Hbc(),Eac),1)}return c}function g3b(a,b,c){var d;Xd(a.n==(Afc(),rec));Xd(b.n==Jec);Xd(c.n==Vcc||H3b(c));d=new O9b(Jdc,a,b,c);Xd(d.n==Jdc);c9b(d,(Hbc(),Fac),1);return d} +function sqb(a){ppb();var b;if(a.n==(Afc(),Jdc)&&iqb(a.g)&&a.n==Jdc&&Gqb(a.c)){b=a.g;return b.n==Xec||b.n==pec||b.g.n==Jdc||b.n==Adc}return false}function JOb(a,b,c){var d;d=(Pd(b.n==(Afc(),Ycc),iSc,b),C8b(b)-1);if(dc.b){kPb(a,c.d);return false}return true}function SOb(a,b){if(!JOb(a,b,(NPb(),sPb)))return false;if(!gPb(a,(Pd(b.n==(Afc(),Ycc),iSc,b),B8b(b,1)))){mPb(a,sPb.d);return false}return true}function ZOb(a,b){if(!JOb(a,b,(NPb(),DPb)))return false;if(!gPb(a, +(Pd(b.n==(Afc(),Ycc),iSc,b),B8b(b,1)))){mPb(a,DPb.d);return false}return true}function s6b(a,b){if(!!b&&!((a.a.a&HPc)==WPc||(a.a.a&HPc)==hMc||(a.a.a&HPc)==eQc)&&!P4b(a.a)){f5b(a.a,b);a.e=true;return true}else return false}function cVb(a,b){var c;c=WUb(a,b);AVb(a,(AZb(),ZXb))&&VVb(a,"A default value cannot be specified after '...'",CE(xE(iW,1),mLc,1,5,[]));return c}function V8b(a){switch(a.n.f){case 29:return a.Wh().length!=0;case 33:case 99:return true;case 26:return V8b(a.c);case 98:default:return false}} +function mF(a,b){if(tF(a))return!!lF[b];else if(a.Gj)return!!a.Gj[b];else if(rF(a))return!!kF[b];else if(qF(a))return!!jF[b];return false}function dc(a){var b,c;c=CE(xE(xF,1),eLc,46,15,[92,117,0,0,0,0]);for(b=0;b<4;b++){c[5-b]=fLc.charCodeAt(a&15);a=a>>4&dLc}return gtc(c,0,c.length)}function _d(a,b,c){if(!a)throw gZ(new Irc(rf("Bad GETELEM node: Expected 2 children but got %s. For node: %s",CE(xE(iW,1),mLc,1,5,[Yrc(b),c]))));}function l5(a,b,c,d){var e,f;if(a.k)for(f=new wxc(a.i);f.a=0)}g=(d=vwc(a.k),d);Xd(g==c)}}function QPb(){NPb();return CE(xE(DR,1),sLc,59,0,[pPb,qPb, +rPb,tPb,uPb,vPb,wPb,xPb,sPb,zPb,yPb,APb,BPb,CPb,DPb,GPb,FPb,EPb,HPb,IPb,JPb,KPb,LPb,MPb])}function n2b(a,b){var c,d,e;d=a.b.n;e=NZ(a.a);if(e.indexOf(".")!=-1){c=Hsc(e,_sc(46));c+d0&&p6b(a.f,c,d,b,d,b+c.length);return new FNb(c,!a.o?mOb(a.j):GMb(a))}function N_(a,b){switch(b.n.f){case 29:case 26:case 27:E_(a,b);break;case 92:q_(b.c,1);N_(a,b.c);break;default:D0("Invalid INC/DEC target "+b.n,b)}}function PTb(a,b){var c,d, +e,f;f=DWb(a.g,0).d.b;c=fUb(a,b);while(AVb(a,(AZb(),wXb))){d=iTb(a,wXb);e=fUb(a,b);c=new RZb(new Z1b(f,a.f),c,d,e)}return c}function QTb(a,b){var c,d,e,f;f=DWb(a.g,0).d.b;c=RTb(a,b);while(AVb(a,(AZb(),BXb))){d=iTb(a,BXb);e=RTb(a,b);c=new RZb(new Z1b(f,a.f),c,d,e)}return c}function RTb(a,b){var c,d,e,f;f=DWb(a.g,0).d.b;c=PTb(a,b);while(AVb(a,(AZb(),EXb))){d=iTb(a,EXb);e=PTb(a,b);c=new RZb(new Z1b(f,a.f),c,d,e)}return c}function HUb(a,b){var c,d,e,f;f=DWb(a.g,0).d.b;c=QTb(a,b);while(AVb(a,(AZb(),yXb))){d= +iTb(a,yXb);e=QTb(a,b);c=new RZb(new Z1b(f,a.f),c,d,e)}return c}function IUb(a,b){var c,d,e,f;f=DWb(a.g,0).d.b;c=HUb(a,b);while(AVb(a,(AZb(),IYb))){d=iTb(a,IYb);e=HUb(a,b);c=new RZb(new Z1b(f,a.f),c,d,e)}return c}function k7b(a,b){if(!!b&&!I4b(a.a)&&!((a.a.a&HPc)==WPc||(a.a.a&HPc)==hMc||(a.a.a&HPc)==eQc)){x5b(a.a,b,$Lc);a.e=true;return true}else return false}function qu(a,b){var c,d;while(a.Id()){if(!b.Id())return false;c=a.Jd();d=b.Jd();if(!(uF(c)===uF(d)||c!=null&&zb(c,d)))return false}return!b.Id()} +function $Kb(a){var b,c;Kd(a.n==(Afc(),Xec));if(!a.c)return false;c=a.c;if(c.n!=Ddc)return false;b=c.c;if(b.n!=Ycc)return false;return _8b(b.c,cQc)}function DNb(a){var b,c;c=0;while(c0&&(uqc(b,b.a.length-1)!=10||d==0)&&(b.a+=" ",b)}function ijc(a,b,c,d){var e,f;e=(!c.b&&(c.b=new fDc),yg(c.b,new Bkc(c,a,b),(Hkc(),Gkc)));if(e)return e.a;f=Xhc(a,b,c,d,1);return zkc(c,a,b,f?Ekc:Fkc)}function xob(a,b){if(b.length== +0)return b;b.indexOf(_Pc)!=-1&&(b=Vsc(b,0,b.lastIndexOf(_Pc)));a.a.Hg(b)&&(b="JSCompiler_"+b);return b+_Pc+a.d+a.f.Cd()}function DMb(a,b,c,d){var e;a.c.Bh(URc+occ("msg.jsdoc.type.record.duplicate",CE(xE(iW,1),mLc,1,5,[b]))+VRc,(e=H8b(a.n,(Hbc(),ybc)),!e?null:e.ug()),c,d)}function sOb(a,b,c){this.o=zE(xF,eLc,46,128,15,1);this.q=zE(yF,iMc,46,3,15,1);Sd(a);this.g=b;this.k=a;this.j=a.length;this.i=this.b=0;this.d=b;this.c=c}function DVb(a){var b;b=DWb(a.g,0);switch(b.e.f){case 2:case 100:case 65:case 43:case 101:case 49:case 52:return true; +default:return OSb(),!!QSb(b.e)}}function HZb(a){var b;b=a;while(b.p==(n1b(),M0b))b=b.a;switch(b.p.f){case 26:case 46:case 55:case 59:case 61:case 74:return true;default:return false}}function mGc(a,b,c,d,e,f,g,h){var i,j;if(!d)return;i=d.a[0];!!i&&mGc(a,b,c,i,e,f,g,h);nGc(a,c,d.d,e,f,g,h)&&b.Pd(d);j=d.a[1];!!j&&mGc(a,b,c,j,e,f,g,h)}function uJc(a,b){var c,d,e;e=new tFc(new eyc(CE(xE(ZY,1),mLc,1113,0,[a,b])),16);d=new zJc(e);c=new KJc(null,d);return kJc(c,new vJc(a)).Dj(new wJc(b))}function ME(a, +b){var c,d,e;if(b<=22){c=a.l&(1<>>0,e.toString(16));f="\\u"+Usc("0000",d.length)+d;g=String.fromCharCode(c);return a.replace(new RegExp(f,"g"),g)}function Ysc(a){var b,c,d;c=a.length;d=0;while(dd&&a.charCodeAt(b-1)<=32)--b;return d>0||b57)return false}return d==1||a.charCodeAt(0)!=48}function ahb(a){_gb();Sd(a);this.b=a;this.f=(!a.P&&(a.P=new llc),a.P);this.g=false;this.i=null;this.e=null;this.a= +null;this.c=null;this.d=null;this.j=null}function Tsb(a,b){Msb();var c;this.b=a;this.d=a.v;this.p=b;this.i=new fxc;this.c=false;this.e=0;this.q=a.G.td;c=a.G.dc;c>0&&c<=100?this.g=c:this.g=100}function Xic(a,b,c,d,e,f){Hhc.call(this,a,e);this.t=new inc;hnc(this.t,this);this.o=b;this.q=d;this.n=f;d||!!c?(Xd(!this.$i()),this.p=c):Tic(this,a.u[38])}function khb(a,b){var c;if(!b)return null;c=I3b(b)?j3b(b):(Yd(H3b(b),b),new L9b((Afc(),Ddc),b));epb(a.f.b,c,new wib(a.a));Xd(!!c.c&&!c.c.f);return g9b(c)} +function $ib(a,b){var c,d,e,f;e=Bpb(b);for(d=new wxc(e);d.a>>4];this.a[b|256]=a.c[b&15]}}function jib(a,b){return F9b(Yjb(x3b(F9b(Yjb(K3b(OPc+(a.r.b==0?"":"$"+a.r.b)), +a.g),b),F9b((n8b(),new Wbc((Afc(),bfc),RPc)),b)),a.r.f.g?a.g.Wi(RPc):null),b)}function mJb(){mJb=JZ;lJb=new nJb("TYPES_ONLY",0);jJb=new nJb("INCLUDE_DESCRIPTIONS_NO_WHITESPACE",1);kJb=new nJb("INCLUDE_DESCRIPTIONS_WITH_WHITESPACE",2)}function hTb(a,b){var c,d,e;d=b.b;c=b.a;if(d.o.a.b' unexpected",CE(xE(iW,1),mLc,1,5,[]));e=b}return e}function dtc(a){var b;b=0;while(0<=(b=a.indexOf("\\",b)))a.charCodeAt(b+1)==36?a=a.substr(0,b)+"$"+Usc(a,++b):a= +a.substr(0,b)+(""+Usc(a,++b));return a}function $$(a){var b,c,d,e,f,g;d=Y$((b=NMc,b));c=(f=x3b(d,(n8b(),new Wbc((Afc(),bfc),"getPrototypeOf"))),f);g=(e=Tqb(c,CE(xE(HU,1),IMc,7,0,[a])),e);return g}function apb(a,b){var c,d;for(c=b.c;c;){d=c.f;if(c.n==(Afc(),fdc)){a.e=b;if(a.a.Bg(a,c,b)){Zob(a,c.c?c.c.i:null,c);a.e=b;a.a.Cg(a,c,b)}}else Zob(a,c,b);c=d}}function lwb(a,b,c,d,e,f){var g;g=mwb(a,b);if(g)return new Gmb(c,null,d,e,g,null,CE(xE(nW,1),uNc,2,6,[b]));return Mmb(c,d,e,f,bwb,CE(xE(nW,1),uNc,2, +6,[b]))}function zHb(){zHb=JZ;yHb=new Mcb("JSC_ILLEGAL_PROTOTYPE_MEMBER",(E2(),C2),new Rtc("Prototype property {0} should be a primitive, not an Array or Object."))}function ZMb(a,b){var c,d;switch(b.f){case 4:d=a.j.n;c=w8b((n8b(),new Xbc((Afc(),cfc),d,a.j.g,lOb(a.j))),a.n);t9b(c,d.length);return c;default:return null}}function mjc(a,b,c,d){!!c.Mi()&&(c=Roc(c.Mi(),a.A.u[52]));ptc((d&&c.xi()&&!c.Bi()&&!c.Ki()&&!c.Ji()&&!c.Ii()&&!c.Mi()&&!_fc(c)&&(b.a+="!",b),c.di(b,d)),"=")}function Osc(a,b,c){var d, +e;d=Psc(b,"([/\\\\\\.\\*\\+\\?\\|\\(\\)\\[\\]\\{\\}$^])","\\\\$1");e=Psc(Psc(c,"\\\\","\\\\\\\\"),"\\$","\\\\$");return Psc(a,d,e)}function pwc(a){var b,c,d;if(a.b!=a.c)return;d=a.a.length;c=Qrc($wnd.Math.max(8,d))<<1;if(a.b!=0){b=nKc(a.a,c);owc(a,b,d);a.a=b;a.b=0}else rKc(a.a,c);a.c=d}function RE(a,b){var c,d,e;e=a.h-b.h;if(e<0)return false;c=a.l-b.l;d=a.m-b.m+(c>>22);e+=d>>22;if(e<0)return false;a.l=c&sMc;a.m=d&sMc;a.h=e&tMc;return true}function y4(a,b){var c,d,e;d=b.Wh();e=E8b(b,(n8b(),j8b))!= +0;if(e)r4(a,E4(a,b.Wh(),e));else{c=Ytc(a.d,d);if(c==null){c=E4(a,b.Wh(),e);_tc(a.d,d,c)}L3(a.c,c)}}function Shb(a,b,c,d){var e,f;$hb(a,b,null);e=new fxc;Ywc(e,mib(c,d));f=Ohb(a);!!f&&Ywc(e,mib(f,d));fib(a,Dhb(a,d,"leaveTryBlock",exc(e,zE(HU,IMc,7,0,0,1))))}function xsb(a,b,c){var d,e;e=(d=vwc(a.f),d);Xd(Esc(b,e.j));Ywc(a.w,e);e.n=c;e.a=0;e.k=1;a.d.a&&(e.d=1);Esc(b,jOc)?vsb(a,e):a.d.a&&a.D!=(Xab(),Wab)&&usb(a,e)}function R6b(a){if((a.a.a&HPc)==WPc||(a.a.a&HPc)==hMc||(a.a.a&HPc)==eQc||(a.a.a&2)!=0|| +W4b(a.a)||(a.a.a&tQc)!=0)return false;a.a.a|=512;a.e=true;return true}function nGc(a,b,c,d,e,f,g){var h,i;if(b.Bj()&&(i=a.a.lf(c,d),i<0||!e&&i==0))return false;if(b.Cj()&&(h=a.a.lf(c,f),h>0||!g&&h==0))return false;return true}function jC(a,b,c,d){var e,f;while(true){f=((d-c)/2|0)+c;e=a.a[f].$f()-b;if(e==0)return f;else if(e<0){c=f+1;if(c>d)return d}else{d=f-1;if(d",gNc),SFb(b))));return new q1(a,c,c)}function cqb(a){ppb();if(a.n==(Afc(),rec))switch(a.g.n.f){case 100:case 88:case 78:return true;case 96:return a.g.c==a;case 65:return dqb(a.g)}return false}function Eqb(a){ppb();var b,c,d;if(a.n==(Afc(),Ycc)){b=a.c;if(b.n==Pdc||b.n==Odc){c=b.c?b.c.i:null;if(!!c&&c.n==bfc){d=c.Wh();return Esc(d,ePc)}}}return false} +function olb(a){var b,c,d;b=a.g;Xd(b.n==(Afc(),Fec));c=b.g;if(c.n==Ccc||c.n==odc){d=c.c.f;if(!d||!V8b(d))return null;return I8b(d)+"."+a.Wh()}else return null}function Uib(a,b,c){var d,e;Xd(c.n==(Afc(),rec));d=c.g;if(d.n==Adc||d.n==Bdc||d.n==Vdc||d.n==Wdc)return;e=Tib(a,Kob(b),c);if(e!=null){k9b(c,Wqb(a.a,e));Vob(b)}}function nXb(a){switch(a){case 9:case 11:case 12:case 32:case 160:case 65279:case 10:case 13:case 8232:case 8233:case 12288:return true;default:return false}}function n6b(a,b,c,d){var e, +f;e=f4b(a.a);if(e){f=new Y5b;Ld(b.charCodeAt(0)!=32&&zsc(b,b.length-1)!=32,mTc);f.d=b;S5b(f,c,d,c,d+b.length);e.a=f;a.e=true}a.b=e}function doc(a,b,c){var d,e,f,g;g=c.Fi().Li();if(!g)return false;f=g.c;for(e=f.Vd();e.Id();){d=e.Jd();if(!!d.Ki()&&eoc(a,b,d.Ki()))return true}return false}function Fhc(a){var b,c,d;if(a.u){b=a.Si();if(!b||b._i()){a.u=false;for(d=a.Ri().Vd();d.Id();){c=d.Jd();if(c.Bi()){a.u=true;break}}}else a.u=b.Bi()}return a.u}function hh(a,b){var c,d,e,f;f=a.Yd();if(f31&&b<127?(d.a+=String.fromCharCode(b),d):wD(d,b)}return d.a}function kZ(a,b){var c;if(pZ(a)&&pZ(b)){c=a/b;if(yMc=65&a<=90|a>=97&a<=122|a==95|a==36;return a==629||(Rqc==null&&(Rqc=new RegExp("[A-Z]","i")),Rqc.test(String.fromCharCode(a)))}function qyc(a,b){var c,d,e;uyc(b);c=b/31|0;d=a.a.length;if(c>=d)return-1;e=(a.a[c]|0)&_Kc<=d)return-1;e=a.a[c]|0}return c*31+Urc(e)} +function Mw(a,b){var c,d;d=a.a.b.b.Yd();if(b==null)for(c=0;c-1){a=a.substr(b+3);c=Hsc(a,_sc(47));c>-1&&(a=a.substr(c+1))}else Esc(a.substr(0,1),"/")&&(a=a.substr(1));return a}function Lqb(a,b){var c,d,e;if(a.n==(Afc(),bfc))return true;if(b.kg().ud){e=a.d;if(e){d=b.mg().u[45];c=new ukc(false);if(Qfc(e,d,0,c))return true}}return false}function gEb(a,b,c){var d,e,f;f=a.g.Ph(c);ZEb();if(Esc(c.substr(0,2),rOc)||Esc(c.substr(0,3),nRc)){e=b;d=e.lastIndexOf("/");f=SFb(e.substr(0,d+1)+(""+f))}return f}function Q6b(a){if(!((a.a.a&HPc)==WPc|| +(a.a.a&HPc)==hMc||(a.a.a&HPc)==eQc)&&!W4b(a.a)&&(a.a.a&2)==0){a.a.a|=512;a.a.a|=bPc;a.e=true;return true}else return false}function np(a){bn();var b,c;if(!a.Id())return mz(),lz;c=a.Jd();if(!a.Id())return new Zz(c);b=new YEc;jDc(b,Sd(c));do jDc(b,Sd(a.Jd()));while(a.Id());return vp(b)}function UE(a,b){var c,d,e,f,g,h,i,j;i=a.h>>19;j=b.h>>19;if(i!=j)return j-i;e=a.h;h=b.h;if(e!=h)return e-h;d=a.m;g=b.m;if(d!=g)return d-g;c=a.l;f=b.l;return c-f}function Rdb(){Rdb=JZ;Qdb=new Mcb("JSC_MISSING_ES6_EXTERNS", +(E2(),B2),new Rtc("Missing externs definition for Symbol. Did you forget to include the ES6 externs?"))}function sCb(a,b,c){var d,e;d=new Jkb;w6(a.b,d);rtb(new utb(a.b,true),null,c);if(SZ(a.b.T)){zrb(new Brb(a.b),b,c);if(a.b.T==1){e=new gsb(a.b);fsb(e,b,c)}}t7(a.b,d)}function iUb(a){var b,c,d;d=DWb(a.g,0).d.b;c=kTb(a);b=null;if(zVb(a,0,(AZb(),iYb))&&Esc(DWb(a.g,0).a,"as")){oTb(a,"as");b=kTb(a)}return new z$b(new Z1b(d,a.f),c,b)}function u3b(a,b,c,d){Xd(a.n==(Afc(),ufc)||a.n==jec||a.n==gdc||a.n==tdc|| +H3b(a));Xd(b.n==tdc||H3b(b));Xd(c.n==tdc||H3b(c));Xd(d.n==Vcc);return new P9b(Fdc,a,b,c,d)}function q3b(a,b,c){if(a.n==(Afc(),rec))Xd(!a.c);else{Xd(a.n==Acc||a.n==Fec);a=new L9b(odc,a)}ae(H3b(b),"%s can't be an expression",b);q8b(a,b);return new L9b(c,a)}function Yqb(a,b){if(a.n==(Afc(),Fec)||a.n==Acc){Sd(b);return F9b(r3b(F9b(new N9b(odc,a,b),a),ufc),a)}else{Xd(a.n==rec&&!a.c);!!b&&q8b(a,b);return F9b(r3b(a,ufc),a)}}function Nic(a,b){var c,d,e,f,g;c=F8b(a.b);if(!!c&&(E8b(c,(Hbc(),qbc))!=0||E8b(c, +Ebc)!=0))return false;for(e=b,f=0,g=e.length;f=65&a<=90|a>=97&a<=122|a>=48&a<=57|a==95|a==36;return iXb(a)||(Pqc==null&&(Pqc=new RegExp("\\d")),Pqc.test(String.fromCharCode(a)))}function Chc(a,b){var c;c=a.Yi(b);if(!c){if(a.si()||a.ni())return a.A.u[11];else if(a.ti()||a.ri()||a.si()||a==a.A.u[64])return a.A.u[58];return a.A.u[51]}return c.e}function EZ(b, +c,d,e){DZ();var f=BZ;$moduleName=c;$moduleBase=d;eZ=e;function g(){for(var a=0;a=0;d--)if(Esc(a[d].d,b)||Esc(a[d].d,c)){a.length>=d+1&&a.splice(0,d+1);break}return a}function Jbb(){Ebb();var a;Fbb(this);a=(wx(0),new nDc);this.f=a;this.d="";this.b=0;this.a=Ibb(this,Cbb);this.c=Ibb(this,Dbb);Wxc(this.a,null);Wxc(this.c,null);Gbb(this)}function nLb(a,b){if($Jb(a.a, +b.c.o)){(b.b.e==(AZb(),dZb)||b.b.e==eZb)&&bKb(a.a,b,(IRb(),WQb));return fKb(a.a,RKb(b.b.e),yKb(a.a,b.a),yKb(a.a,b.c))}else return oLb(a,b)}function Lkc(a,b,c){var d,e,f,g;if(pF(c,48)&&c.Xi()!=null){g=gFc(a.f,b,new plc);f=c;g.Je(f.Xi(),f)}else if(c.Mi())for(e=Ooc(c.Mi()).Vd();e.Id();){d=e.Jd();Lkc(a,b,d)}}function wg(a,b,c){var d,e,f;for(e=a.He().Vd();e.Id();){d=e.Jd();f=d.Ve();if(uF(b)===uF(f)||b!=null&&zb(b,f)){if(c){d=new wvc(d.Ve(),d.We());e.Kd()}return d}}return null}function I_(a,b){var c,d, +e;b0((Afc(),Pdc),b);d=Cfc(b.n);d!=-1&&(c=C8b(b),d!=c&&D0(QMc+d+RMc+c,b));E_(a,b.c);e=b.c?b.c.i:null;b0(bfc,e);c0(e)&&e.Wh().length==0&&D0(WMc,e)}function gLb(a,b,c){a.a.a.d!=(zJb(),xJb)&&a.a.d.Bh("type syntax is only supported in ES6 typed mode: "+c,a.a.q,(RJb(),b.o.b.b+1),b.o.b.a);a.a.e=oQb(a.a.e,c);qKb(a.a,b.o)}function xdb(a){var b,c,d,e;b=I8b(Ppb(a));c=Sd((ppb(),Gpb(a,new jrb)));e=null;for(d=c.f;d;d=d.f){e=wdb(d,b);if(e)break}return Td(e,"$jscomp.inherits() call not found.")}function aUb(a,b){var c, +d,e,f;f=DWb(a.g,0).d.b;c=IUb(a,b);if(AVb(a,(AZb(),UYb))){iTb(a,UYb);d=KTb(a,b);iTb(a,NXb);e=KTb(a,b);return new p$b(new Z1b(f,a.f),c,d,e)}return c}function kLb(a,b){var c,d,e,f;bKb(a.a,b,(IRb(),EQb));f=dKb(a.a,(Afc(),Acc));for(d=b.a.Vd();d.Id();){c=d.Jd();c.p==(n1b(),$_b)?e=uLb(a,c):e=AKb(a.a,c);q8b(f,e)}return f}function k4b(a,b){!a.d&&(a.d=new L5b);if(b5b(a,b)||(a.a&HPc)==hMc)return false;if(!a.d.r)a.d.r=new fxc;else if(_wc(a.d.r,b,0)!=-1)return false;Ywc(a.d.r,b);return true}function oyc(a){var b, +c,d,e;d=xyc(a.a);b=-2128831035^d;for(c=0;c<=d;c++){e=a.a[c]|0;b=b*LTc&-1^e&255;b=b*LTc&-1^e>>>8&255;b=b*LTc&-1^e>>>16&255;b=b*LTc&-1^e>>>24}return b}function zg(a){var b,c,d;d=new hGc(iLc,"{","}");for(c=a.He().Vd();c.Id();){b=c.Jd();eGc(d,Ag(a,b.Ve())+"="+Ag(a,b.We()))}return!d.a?d.c:d.e.length==0?d.a.a:d.a.a+(""+d.e)}function spb(a){var b,c,d,e;c=a.c;e=new stc;for(d=c;d;d=d.f){b=d.n==(Afc(),Aec)||Nqb(d)||d.n==tdc?"":Ypb(d);if(b==null)return null;d!=c&&(e.a+=",",e);e.a+=""+b}return e.a}function Uqc(a, +b){if(b<2||b>36)return-1;if(a>=48&&a<48+$wnd.Math.min(b,10))return a-48;if(a>=97&&a=65&&a=65&&b<=90&&(c[d]=(b^32)&dLc)}return gtc(c,0,c.length)}return a}function $b(a){var b,c,d,e;e=a.length;for(d=0;d=97&&b<=122&&(c[d]=(b^32)&dLc)}return gtc(c,0,c.length)}return a}function Bt(a,b){wt();var c,d;Sd(a);if(!b.Id())return Sd(a),vt==a?ut:new oz(new DHc(a));c=new DHc(a);while(b.Id()){d=b.Jd();Sd(d);c.a.Je(d,(Nqc(),Lqc))==null}return new oz(c)}function ulb(a,b,c,d,e,f){var g;if(ylb(a,b,c,d,e,f))return;g=plb(a,f);E8b(d,(n8b(), +e8b))!=0;e.n==(Afc(),Ccc)&&e.g.n!=Ddc?klb(a,g,d,(cmb(),amb),b,c):jlb(a,g,d,(cmb(),amb),b,c)}function w7b(a){if((a.a.a&HPc)==WPc||(a.a.a&HPc)==hMc||(a.a.a&HPc)==eQc||W4b(a.a)||(a.a.a&JPc)!=0||(a.a.a&vMc)!=0||(a.a.a&IPc)!=0)return false;a.a.a|=IPc;a.e=true;return true}function Ync(a,b){var c,d,e;e=(Bn(),new Zr);Yr(e,a.c.wf(0,a.d.Yd()));for(c=a.d.Yd();c0,HLc,c); +d=Xtc(a.a,b);if(!d)return 0;g=d.a;if(g>c)f=c;else{f=g;auc(a.a,b)}d.a+=-f;a.b=vZ(a.b,f);return g}function tEb(){tEb=JZ;sEb=new Mcb("JSC_INVALID_AMBIGUOUS_PATH",(E2(),B2),new Rtc('The path "{0}" is invalid. Expected any of the following prefixes for non-relative paths: {1}.'))}function rKb(a,b){var c,d;d=k6b(a.f);!!d&&(d9b(b,(Hbc(),mbc),d),b);if(a.g){if(!!H8b(b,(Hbc(),mbc))&&D4b(H8b(b,mbc))!=null){c=D7b(a.g);T6b(c,D4b(H8b(b,mbc)));a.g=k6b(c)}q9b(b,a.g)}}function hLb(a){var b,c,d;c=null;while(ZKb(a.c)){b= +g9b(a).c.Wh();!c&&(c=new Rs);Ywc(c.b,Sd(b))}if(c){d=np(new wxc(c.b));d.Yd()==1&&d.Sd(ANc)&&(d=(RJb(),QJb));d9b(a,(Hbc(),Qac),d)}}function H$b(a){IZb.call(this,(n1b(),m0b),a.k);this.k=a.n;this.d=a.d;this.i=a.i;this.f=a.f;this.g=a.g;this.a=a.a;this.j=Sd(a.j);this.b=Sd(a.b);this.n=a.o;this.c=Sd(a.c);this.e=a.e}function e9b(a,b){var c,d,e;ce(b.g==a,qTc,a,b);Sd(b.i);c=a.c.i;e=b.i;d=b.f;if(a.c==b){a.c=d;!!d&&(d.i=c)}else if(b==c){a.c.i=e;e.f=null}else{e.f=d;d.i=e}b.f=null;b.i=null;b.g=null}function MEb(){MEb= +JZ;KEb=new NEb("GOOG_REQUIRE_SYMBOL",0);JEb=new NEb("ES6_IMPORT",1);LEb=new NEb("PARSED_FROM_DEPS",2);HEb=new NEb("COMMON_JS",3);IEb=new NEb("COMPILER_MODULE",4)}function P3b(a){var b,c,d,e,f;b=new J9b((Afc(),Eec));for(d=a,e=0,f=d.length;e1&&D0("Expected at most 1 'default' in switch but was "+ +d,b)}function G6(a){var b;if(a.n==(Afc(),Xec))return a;b=Fpb(a.g);if(!b)throw gZ(new Irc("An enclosing scope is required for change reports but node "+a+" doesn't have one."));return b}function OBb(a,b,c,d){var e,f,g,h,i;h=wjc(c);g=vjc(c);e=Hn(xjc(c));for(f=0;fa.length||e+f>d.length)return false;if(f<=0)return true;g=a.substr(c,f);h=d.substr(e,f);return b?Fsc(g,h):Esc(g,h)}function v0(a,b){var c,d,e;F_(a,(IRb(),CRb),b);b0((Afc(),ifc),b);for(c=b.c;c;c=c.f)c.n==jfc?w0(c):(b0(kfc,c),d=Cfc(c.n),d!=-1&&(e=C8b(c),d!=e&&D0(QMc+d+RMc+e,c)),E_(a,c.c),undefined)}function wdb(a,b){var c, +d,e;if(a.n!=(Afc(),Ddc))return null;c=a.c;if(c.n!=Ycc)return null;e=c.c;if(!a9b(e,bOc,bOc.length))return null;d=Sd(e.f);return a9b(d,b,b.length)?Sd(d.f):null}function agb(a,b){var c,d,e;c=E8b(a,(Hbc(),zbc))!=0?x8b(b.g,false):x8b(b.d,false);Z8b(c);if(a.n==(Afc(),fdc))return I9b((e=w3b(c,g9b(a)),e),a);else{d=a.c.c;return G9b(O$(c,a.Wh()),d)}}function Jgb(a,b,c,d,e,f){Ld((ppb(),f.n!=(Afc(),pec)&&Jqb(f.g)),f);switch(c.n.f){case 93:Hgb(a,b,c,d,e,f);break;case 94:Igb(a,b,c,d,e,f);break;default:throw gZ(new Irc("unexpected")); +}}function cpb(b,c,d){var e,f;try{e=c.g;Sd(e);Qob(b,e);b.e=e;Sob(b,e);Zob(b,c,e);Xd(d.g==e);Zob(b,d,e);Rob(b,false)}catch(a){a=fZ(a);if(pF(a,80)||pF(a,38)){f=a;Xob(b,f)}else throw gZ(a);}}function eqb(a,b){ppb();var c;c=b.g;if(c.n==(Afc(),Pdc)&&b==(c.c?c.c.i:null))return a.Gg(b.Wh());else if(Sqb(b))return a.Gg(b.Wh());else if(b.n==rec)return a.Fg(b.Wh());return false}function gqb(a){ppb();switch(a.n.f){case 72:case 73:case 101:case 102:case 71:case 70:case 77:case 66:case 83:case 44:case 78:case 67:case 68:case 69:return true; +default:return false}}function Evb(a,b,c){var d,e,f,g,h;h=b.c;while(!!h&&h!=c){g=h.f;ppb();if(h.n==(Afc(),Ddc)&&h.c.n==Ycc){d=h.c;e=d.c;if(a9b(e,WQc,WQc.length)){f=BQb(e.f.f.f.Wh());Dvb(a,f)&&crb(b,h)}}h=g}}function bFb(a,b,c,d,e){ZEb();Sd(a);Sd(b);Sd(d);Sd(e);this.f=d;this.e=e;this.a=new sFb;this.d=cFb(a,e);this.b=eFb(Wt(Wt(b,new fFb),d),this.d,e);this.c=c.Nh(this.b,this.d,this.a,this.e)}function xUb(a){var b,c,d,e;e=DWb(a.g,0).d.b;iTb(a,(AZb(),jYb));iTb(a,GYb);b=xTb(a,1);iTb(a,LXb);d=hVb(a);c=null; +if(AVb(a,WXb)){iTb(a,WXb);c=hVb(a)}return new d_b(new Z1b(e,a.f),b,d,c)}function rUb(a){var b,c,d;d=DWb(a.g,0).d.b;iTb(a,(OSb(),hSb).b);c=!!mTb(a,(AZb(),bZb));b=P$b(new T$b((Y$b(),V$b)),jTb(a));sUb(a,b,c?(pWb(),nWb):(pWb(),oWb));return I$b(b,new Z1b(d,a.f))}function _sc(a){var b,c;if(a>=kMc){b=55296+(a-kMc>>10&mMc)&dLc;c=56320+(a-kMc&mMc)&dLc;return String.fromCharCode(b)+(""+String.fromCharCode(c))}else return String.fromCharCode(a&dLc)}function lkb(a,b,c){var d;if(b.n==(Afc(),Ccc)){d=b.c;switch(d.n.f){case 29:return true; +case 26:return!kkb(a,d.c,c);case 27:return!kkb(a,d.c,c)&&!kkb(a,d.c?d.c.i:null,c)}}return false}function ykb(a,b){var c;c=Akb(a,b);Esc(c.substr(0,5),"goog.")&&u8((!a.d&&!!a.i&&(a.d=L6(a.c,a.i)),a.d),(wDb(),vDb));u8((!a.d&&!!a.i&&(a.d=L6(a.c,a.i)),a.d),ADb(c,(b.c?b.c.i:null).Wh()))}function ysb(a,b,c){this.d=new oub;this.f=new Cwc;this.w=new fxc;Ld(c!=(Xab(),Uab),"PerformanceTracker can't work without tracer data.");this.H=mZ(Date.now());this.n=a;this.u=b;this.D=c}function SEb(a,b){var c;if(Ztc(a.b, +b))return Ytc(a.b,b);return pEc(a.e,(RFb(),UFb((c=Osc(Osc(Osc(Osc(Osc(Nsc(Nsc(b,58,45),92,47)," ",cNc),"[",dNc),"]",eNc),"<",fNc),">",gNc),SFb(c)))))}function aPb(a,b,c){var d;for(d=0;d=0){f=b.substr(0,c);e=plb(a,f);d=Blb(e,b.substr(c+1))}else{d=new Ilb(b,null);Ywc(a.a.f,d)}_tc(a.a.g,b,d)}return d}function Qsb(a,b,c){var d,e;a.k=0;a.o=0;if(a.n){a.o=(a.n.b-a.n.a)/a.i.a.length;a.k=a.n.a}for(e=new wxc(a.i);e.a0?1:0;while(f.a[e]!=c){f=f.a[e]; +e=a.a.lf(c.d,f.d)>0?1:0}f.a[e]=d;d.b=c.b;d.a[0]=c.a[0];d.a[1]=c.a[1];c.a[0]=null;c.a[1]=null}function Wc(a){var b,c,d;d=new hGc(iLc,"[","]");for(c=a.Vd();c.Id();){b=c.Jd();eGc(d,uF(b)===uF(a)?"(this Collection)":b==null?jLc:NZ(b))}return!d.a?d.c:d.e.length==0?d.a.a:d.a.a+(""+d.e)}function qNb(a,b){var c;if(b==(fOb(),WNb)){xNb(a);c=pNb(a,!a.o?mOb(a.j):GMb(a));if(c){xNb(a);OMb(a,aOb)?!a.o?mOb(a.j):GMb(a):(CMb(a,YRc,a.j.g,lOb(a.j)),null)}return c}else return pNb(a,b)}function k0(a,b){var c;b0((Afc(), +Jec),b);for(c=b.c;c;c=c.f)if(c.n==Sec)n0(a,Jec,c);else if(c.n==mdc){F_(a,(IRb(),SQb),c);x_(a,Jec,c)}else c.n==rec?W_(c):c.n==Acc?l_(a,Jec,c):h0(a,Jec,c)}function usb(a,b){var c,d,e;e=xpb(a.u);b.b=a.b-e;a.b=b.c=e;if(!(a.D==(Xab(),Vab)||a.D==Sab))return;c=Gsb((d=a.u,a.D==Sab,d));e=c.b;b.e=a.e-e;a.e=b.o=e;if(a.D==Sab){e=0;b.f=a.o-e;a.o=b.g=e}}function Psb(b,c,d,e){var f;if(!b.r)return;try{tCb(W7(b.b),d,e);I1(b.a,c,b.d)}catch(a){a=fZ(a);if(pF(a,38)){f=a;throw gZ(new Jrc("Validity checks failed for pass: "+ +c,f));}else throw gZ(a);}}function KUb(a,b,c,d,e,f){var g;g=M$b(R$b(L$b(O$b(J$b(Q$b(N$b(S$b(P$b(new T$b((Y$b(),X$b)),c),d),e),false),f),tTb(a)),pUb(a,1)),sTb(a)),new w$b(new Z1b(b,a.f)));return I$b(g,new Z1b(b,a.f))}function wjc(a){var b,c,d,e,f;b=0;c=0;for(e=(f=a.b.a,f?!f.c?(Cyc(),Cyc(),Byc):new Kbc(f.c):(Cyc(),Cyc(),Byc)).Vd();e.Id();){d=e.Jd();++b;E8b(d,(Hbc(),qbc))==0&&E8b(d,Ebc)==0&&(c=b)}return c}function Hxc(a,b){var c;if(uF(a)===uF(b))return true;if(a==null||b==null)return false;if(a.length!= +b.length)return false;for(c=0;ca.f.b,b||zVb(a,0,ZYb)||zVb(a,0,KXb)||zVb(a,0,XXb)?nTb(a):iTb(a,OXb))}return In(c.a)}function s5(a){if(a.o<=a.p)return;if(a.d>a.b&&a.d=a.a.length)return pyc(a)-1;d=(a.a[c]|0)&_Kc>>>31-b%31-1;while(d==0){if(--c<0)return-1;d=a.a[c]|0}return c*31+(32-Trc(d))-1}function EIc(a,b){var c,d,e,f,g,h,i;for(d=IIc(a),f=0,h=d.length;f>22-b;e=a.h<>22-b}else if(b<44){c=0;d=a.l<>44-b}else{c=0;d=0;e=a.l<0,HLc,c);d=Xtc(a.a,b);if(!d){g=0;$tc(a.a,b,new _l(c))}else{g=d.a;f=hZ(g,c);Od(iZ(f,_Kc)<=0,f);d.a+=c}a.b=hZ(a.b,c);return g}function VC(a,b,c){var d, +e,f,g;ZB(a.b,c-a.c);a.c=c;if(b){e=MC(a.j,b.d);ZB(a.b,e-a.g);a.g=e;g=b.c.b;f=b.c.a;ZB(a.b,g-a.i);a.i=g;ZB(a.b,f-a.f);a.f=f;if(b.b!=null){d=LC(a.j,b.b);ZB(a.b,d-a.e);a.e=d}}}function LD(a){var b;if(a.c==null){b=uF(a.b)===uF(JD)?null:a.b;a.d=b==null?jLc:sF(b)?b==null?null:b.name:tF(b)?oMc:frc(Bb(b));a.a=a.a+": "+(sF(b)?b==null?null:b.message:b+"");a.c="("+a.d+") "+a.a}}function a_(a){var b,c,d,e;b=Sd(su(Ue(a.a,a.b)));d=(Sd(a),Ld(true,"number to skip cannot be negative"),new du(a));c=(e=(ae(Hsc(b,_sc(46))== +-1,HMc,b),n8b(),new Wbc((Afc(),rec),b)),e);return P$(c,d)}function PCb(a,b,c){ICb();var d,e;if(b.g.n==(Afc(),ufc)&&(d=H8b(b,(Hbc(),ybc)),!!d&&d.Kh())&&Dqb(b))return true;return MBb(),e=J6(a,Lmb(c,BBb,CE(xE(nW,1),uNc,2,6,["dummy","dummy"]))),e==(E2(),C2)}function p2b(a){var b,c;if(a.a==null)return a.b.k=false,j2b(a,jLc,0);b=null;if(pF(a.a,156))b=new XBc(a.a.a);else if(pF(a.a,294))b=a.a;else throw gZ(g2b(a));c=new stc;c.a+=""+b;return j2b(a,c,0)}function Znc(a,b){var c,d;if(b.Yd()==a.c.Yd())return b; +else if(b.Yd()>a.c.Yd())return b.wf(0,a.c.Yd());else{c=(Bn(),new Zr);Qr(c,b);for(d=b.Yd();d=(e/2|0)){this.e=!d?null:d.c;this.d=e;while(c++0)zw(this)}this.b=b;this.a=null}function $E(a,b){var c,d,e,f;b&=63;c=a.h&tMc;if(b<22){f=c>>>b;e=a.m>>b|c<<22-b;d=a.l>>b|a.m<<22-b}else if(b<44){f=0;e=c>>>b-22;d=a.m>>b-22|a.h<<44-b}else{f=0;e=0;d=c>>>b-44}return HE(d&sMc,e&sMc,f&tMc)}function _ib(a, +b,c,d){var e,f,g,h;for(f=v8b(c.c).Vd();f.Id();){e=f.Jd();g=e.c.Wh();h=i$(Kob(b),g);if(!!h&&uqb(h.c)){g=Tib(a,Kob(b),e.c);Sd(g)}qGc(a.b,(e.c?e.c.i:null).Wh(),new Mib(g,e))}e9b(d,c);Vob(b)}function fjb(){fjb=JZ;djb=new Mcb("BAD_REST_PARAMETER_ANNOTATION",(E2(),D2),new Rtc('Missing "..." in type annotation for rest parameter.'));ejb=rQb((iQb(),TPb),CE(xE(FR,1),sLc,24,0,[(IRb(),xRb),zRb]))}function Ljb(a,b,c,d,e){var f,g;f=Yjb(f3b(CE(xE(HU,1),IMc,7,0,[])),b);for(g=a.c;g;g=g.f)g.n==(Afc(),jfc)&&(g.Th()!= +null?q8b(f,Yjb(V3b(g.Th()),c)):q8b(f,Yjb($3b(Yjb((n8b(),new tac(0)),e)),d)));return f}function Kpb(a,b){ppb();var c,d;Xd(a.n==(Afc(),Eec));for(d=(!a.c?(Cyc(),Cyc(),Byc):new Kbc(a.c)).Vd();d.Id();){c=d.Jd();if(c.n==fdc&&c.c.Xh(b,false,true,false,false))return c.c?c.c.i:null}return null}function u2b(a,b){var c,d;d=a.b.n;d=0==d?1:d;M2b(a.b,d);c=Erc(a.a);if(c==0){--d;M2b(a.b,d);n2b(a,b);return}c=$wnd.Math.abs(c);if(!isNaN(c)&&!isFinite(c)){d=a.b.n;--d;M2b(a.b,d);m2b(a,b);return}n2b(a,b)}function Ov(a, +b){var c;b.d?b.d.b=b.b:a.a=b.b;b.b?b.b.d=b.d:a.e=b.d;if(!b.e&&!b.c){c=auc(a.b,b.a);c.a=0;++a.c}else{c=Xtc(a.b,b.a);--c.a;!b.e?c.b=b.c:b.e.c=b.c;!b.c?c.c=b.e:b.c.e=b.e}--a.d}function AWb(a){var b;b=(DWb(a,0),axc(a.d,0));(!xWb(a,a.f)||b.e!=(AZb(),KXb))&&EWb(a,wWb(a,a.f),"Expected '}' after expression in template literal",CE(xE(iW,1),mLc,1,5,[]));return BWb(a,(AZb(),lZb),kZb)}function FDc(a,b,c){var d,e,f,g;g=b==null?0:a.b.sj(b);e=(d=a.a.get(g),d==null?new Array:d);if(e.length==0)a.a.set(g,e);else{f= +CDc(a,b,e);if(f)return f.Xe(c)}e[e.length]=new wvc(b,c);++a.c;return null}function Lrb(a,b){var c,d,e,f;if(b.c.n==(Afc(),rec)){e=b.c;A9b(b,Rpb(b));f=b.g;d=new J9b(tdc);j9b(f,b,d);c=F9b(h3b(F9b(e.Sh(false),e),b),b);q9b(c,H8b(b,(Hbc(),mbc)));d9b(b,mbc,null);j9b(f,d,c);w7(a.b,c)}}function $Fb(a,b,c){var d,e,f,g,h,i,j;for(f=WFb,g=0,h=f.length;gc)return Id(a,c,"start index");if(b<0||b>c)return Id(b,c,"end index");return rf("end index (%s) must not be less than start index (%s)",CE(xE(iW,1),mLc,1,5,[Yrc(b),Yrc(a)]))}function R_(a,b,c){switch(c.n.f){case 29:W_(c);break;case 93:l_(a,b,c);break;case 94:h0(a,b,c);break;case 26:case 27:J_(a, +b,c);break;case 92:R_(a,b,(Xd(!!c.c&&!c.c.f),c.c));break;default:D0(SMc+b+TMc,c)}}function okb(a,b,c,d){Sd(a);Sd(b);Sd(c);this.b=a;this.d=b;this.c=c;this.e=d;this.a=true;this.i=(!a.P&&(a.P=new llc),a.P).u[51];this.j=(!a.P&&(a.P=new llc),a.P).u[52];this.f=(!a.P&&(a.P=new llc),a.P).u[45]}function vpb(a){var b;Pd(a.n==(Afc(),xec),"Expected NEW node, got %s",a.n);if(Q9b(E8b(a,(Hbc(),wbc)),15))return false;if(Q9b(E8b(a,wbc)&15,11)&&rpb(a))return false;b=a.c;return b.n!=rec||!hpb.Sd(b.Wh())}function xKb(a){var b; +switch(a.p.f){case 6:case 20:case 67:case 77:return false;case 49:case 43:case 42:case 46:case 55:case 45:b=KKb(a);if(b.p==(n1b(),M0b))return false;return true;default:return true}}function nOb(a){var b,c;for(;;){b=jOb(a);switch(b){case 42:if((c=jOb(a),a.q[a.r++]=c,--a.b,c)!=47){iOb(a,b);break}case -1:case 10:a.q[a.r++]=b;--a.b;a.n=gtc(a.o,0,a.p);a.p=0;return a.n;default:iOb(a,b)}}}function v_(a,b){var c,d,e,f;e=Cfc(b.n);e!=-1&&(d=C8b(b),e!=d&&D0(QMc+e+RMc+d,b));c=b.n;f=b.c;switch(f.n.f){case 29:W_(f); +break;case 26:case 27:J_(a,c,f);break;default:D0(SMc+c+TMc,f)}E_(a,b.c?b.c.i:null)}function Lhb(a,b,c,d){var e,f;Bhb(a,b,c);e=new fxc;if(!b){f="setFinallyBlock";Ywc(e,mib(c,d))}else{f="setCatchFinallyBlocks";Ywc(e,mib(b,d));!!c&&Ywc(e,mib(c,d))}eib(a,Dhb(a,d,f,exc(e,zE(HU,IMc,7,0,0,1))))}function Avb(){Avb=JZ;zvb=new Mcb("JSC_INSUFFICIENT_OUTPUT_VERSION",(E2(),C2),new Rtc("Built-in ''{0}'' not supported in output version {1}"));yvb=(bn(),pp(CE(xE(iW,1),mLc,1,5,["goog.global.","window."])))}function dMb(a, +b){var c,d,e,f;bKb(a.a,b,(IRb(),CRb));f=dKb(a.a,(Afc(),ifc));T9b(f.k)==-1&&vKb(f,b.o.b,b.o.a);e=!b.b?f:fKb(a.a,hfc,yKb(a.a,b.b),f);for(d=b.a.Vd();d.Id();){c=d.Jd();q8b(f,yKb(a.a,c))}return e}function Qoc(a,b){var c,d,e,f;if(!b.Bi()&&!b.Mi())for(f=0;f1&&(new otb,Tt(Wt(new eyc(a.B.a),new lnb)));for(c=Hn(Tt(Wt(new eyc(a.B.a), +new lnb))).Vd();c.Id();){b=c.Jd();W6(a,b)&&(d=true)}d&&(D6(a,a.B?new eyc(a.B.a):null),Z6(a))}function ULb(a,b){var c,d,e,f;c=QLb(a,b.a);A9b(c,(Afc(),cfc));d=b.b;if(!d){f=MLb(a,b.a);c9b(c,(Hbc(),kbc),1)}else f=(d.p==(n1b(),$_b)?e=uLb(a,d):d.p==q0b?e=MLb(a,d.a):e=AKb(a.a,d),e);r8b(c,f);return c}function gsc(a,b){var c,d,e,f,g;g=1<=0;d--){i= +$wc(a.b,d);for(e=i.Yd()-1;e>=0;e--){f=i.ce(e);if(b!=f&&nyc(a.c[b.c],f.c)&&c!=f&&nyc(a.c[c.c],f.c))return f}}return null}function RJb(){RJb=JZ;QJb=(bn(),new Zz(ANc));NJb=QJb;OJb=tp(MNc,XOc,ZNc,INc,NNc,LNc,CE(xE(nW,1),uNc,2,6,[DMc]));PJb=tp(MNc,XOc,ZNc,INc,NNc,LNc,CE(xE(nW,1),uNc,2,6,[DMc,ONc,YNc,"let",FRc,GRc,HRc,IRc,KRc,RNc]))}function Toc(a,b,c,d){var e,f;if(b.Bi()||Yoc(new spc,a.b))return true;if(b.mi())return true;for(f=0;f=c?new _B(f+1,e-c):a;else if(f==b)return new _B(f-1,e+c);else if(f>b)throw gZ(new Irc("Cannot undo line cut on a previous line."));else return a}function iNb(a,b){var c,d;d=QMb(a,(Afc(),hec));c=_Mb(a,b);if(!c)return CMb(a,_Rc,a.j.g,lOb(a.j)),null;xNb(a);if(!OMb(a,(fOb(),aOb)))return CMb(a,YRc,a.j.g,lOb(a.j)),null;!a.o?mOb(a.j):GMb(a);q8b(d,c);return d}function J8b(a,b){var c,d,e;e=(a.c?a.c.i:null).Wh();b+=1+e.length;if(a.c.n==(Afc(),Pdc)){c=J8b(a.c,b);if(!c)return null}else{d=I8b(a.c);if(d== +null)return null;c=new ttc;c.a+=""+d}ptc((c.a+=".",c),e);return c}function Txc(a,b,c,d,e,f){var g,h,i,j;g=d-c;if(g<7){Qxc(b,c,d,f);return}i=c+e;h=d+e;j=i+(h-i>>1);Txc(b,a,i,j,-e,f);Txc(b,a,j,h,-e,f);if(f.lf(a[j-1],a[j])<=0){while(ca.a.qj())return;c=(d=a.a.qj(),d>=1E3?"error":d>=900?"warn":d>=800?"info":"log");uKc(c,a.b);!!a.c&&vKc(b,c,a.c,"Exception: ",true)}function X6(a){var b,c,d;d=false;a.G.cc>1&&(new otb,Tt(Wt(new eyc(a.B.a),new lnb)));for(c=Hn(Tt(Wt(new eyc(a.B.a), +new lnb))).Vd();c.Id();){b=c.Jd();if(y8(b).b){Qmb(b.k,b);d=true}}d&&(D6(a,a.B?new eyc(a.B.a):null),Z6(a))}function Fub(a,b,c){this.f=a;Cub(this);this.b=Sd(c);c.n==(Afc(),Jdc)&&E8b(c,(Hbc(),Gac))!=0?c.n==Jdc&&E8b(c,(Hbc(),Fac))!=0?this.a=!b.a?this:b.a:this.a=this:c.n==Jdc&&E8b(c,(Hbc(),Fac))!=0?this.a=b.a:this.a=null}function jNb(a){xNb(a);if(!OMb(a,(fOb(),ONb)))return QMb(a,(Afc(),tdc));!a.o?mOb(a.j):GMb(a);xNb(a);if(OMb(a,eOb)&&Esc(XNc,a.j.n)){!a.o?mOb(a.j):GMb(a);return QMb(a,(Afc(),vfc))}else return mNb(a, +!a.o?mOb(a.j):GMb(a))}function DTb(a){var b;b=(Bn(),new Zr);while(zVb(a,0,(AZb(),vZb))||zVb(a,0,sYb)||zVb(a,0,PXb)||zVb(a,0,gYb)||zVb(a,0,IXb)||zVb(a,0,oYb)||zVb(a,0,YXb)||zVb(a,0,wYb)||zVb(a,0,xYb)||zVb(a,0,bYb))Xr(b,CTb(a));return In(b.a)}function Itc(){Itc=JZ;Htc=new Jtc("UP",0);Btc=new Jtc("DOWN",1);Atc=new Jtc("CEILING",2);Ctc=new Jtc("FLOOR",3);Ftc=new Jtc("HALF_UP",4);Dtc=new Jtc("HALF_DOWN",5);Etc=new Jtc("HALF_EVEN",6);Gtc=new Jtc("UNNECESSARY",7)}function Vf(a,b,c){var d;d=a.c.Ie(b);if(!d){d= +a.ze(b);if(d.Pd(c)){++a.d;a.c.Je(b,d);return true}else throw gZ(new Kqc("New Collection violated the Collection spec"));}else if(d.Pd(c)){++a.d;return true}else return false}function nt(a,b){ht();var c,d,e,f;d=Sd(b.Ve());f=Sd(b.We());c=d;if(kGc(a,c)){e=(new Tvc(new aHc(a,(oHc(),nHc),d))).b.vj().Jd();throw gZ(new qnb("Duplicate keys in mappings "+e.Ve()+"="+e.We()+" and "+d+"="+f));}qGc(a,d,f)}function nhb(a,b,c,d){var e,f,g;g=new pib(a.a);c=Thb(a.a,c);d=Thb(a.a,d);cib(a.a,g);e=g9b(b);Yhb(a.a,c,d); +thb(a,e,null,null);Vhb(a.a);cib(a.a,d);f=khb(a,jhb(a,g9b(b)));eib(a.a,F9b(B3b(f,Ghb(a.a,g,false,b)),b));cib(a.a,c)}function ayc(a){var b,c,d,e,f;if(a==null)return jLc;f=new hGc(iLc,"[","]");for(c=a,d=0,e=c.length;da.f.b;if(b||zVb(a,0,ZYb)||zVb(a,0,KXb)||zVb(a,0,XXb))return;VVb(a,"Semi-colon expected",CE(xE(iW,1),mLc, +1,5,[]))}function T2b(a){if(a.indexOf(":")!=-1||a.indexOf("=")!=-1){if(Hsc(a,_sc(58))==-1)return Hsc(a,_sc(61));if(Hsc(a,_sc(61))==-1)return Hsc(a,_sc(58));return $wnd.Math.min(Hsc(a,_sc(61)),Hsc(a,_sc(58)))}return Hsc(a,_sc(32))}function QLb(a,b){var c;if(!b)return iKb(a.a,(Afc(),rec),RRc);else if(b.e==(AZb(),iYb))c=LLb(a,b,true);else if(b.e==DYb){c=BKb(a.a,b);c9b(c,(n8b(),i8b),1)}else{c=$Lb(a,b);c9b(c,(n8b(),i8b),1)}Xd(c.n==(Afc(),bfc));return c}function Py(a,b){var c,d,e;if(b===a)return true;if(pF(b, +182)){e=b;if(VB(a.b)!=VB(e.b)||Zi(a).Yd()!=Zi(e).Yd())return false;for(d=Zi(e).Vd();d.Id();){c=d.Jd();if(nj(a,c.b.Ve())!=Nj(c))return false}return true}return false}function Hbb(a){var b,c,d;do{c=a.d;b=a.b;if(c.length==0){d=b%a.a.length;c=String.fromCharCode(a.a[d].a);b=b/a.a.length|0}while(b>0){--b;d=b%a.c.length;c+=nF(a.c[d].a);b=b/a.c.length|0}++a.b}while(Efc(c)||kDc(a.f,c));return c}function Bvb(a,b){var c,d,e,f,g,h,i;i=new Svb(a);epb(a.a,b,i);if(i.a.a.Yd()!=0){d=null;for(f=(g=(new Wuc(i.a.a)).a.He().Vd(), +new avc(g));f.a.Id();){e=(c=f.a.Jd(),c.Ve());d=C6(a.a,e,false)}if(d){h=d.g;Evb(a,h,d.f);w7(a.a,h)}}}function GLb(a,b){var c,d,e;bKb(a.a,b,(IRb(),iRb));c=pMb(a,b.a,b);if(!b.d){d=oMb(a,(Afc(),Xdc),b.b);T9b(d.k)==-1&&vKb(d,b.o.b,b.o.a)}else{d=iKb(a.a,(Afc(),Ydc),b.d.a);tKb(d,b.d)}e=$Lb(a,b.c);return gKb(a.a,(Afc(),Vdc),c,d,e)}function zUb(a){var b,c,d;d=DWb(a.g,0).d.b;c=kTb(a);b=null;if(zVb(a,0,(AZb(),iYb))&&Esc(DWb(a.g,0).a,"as")){oTb(a,"as");b=jTb(a)}else qTb(a,c.a)&&SVb(a,null,SSc,CE(xE(iW,1),mLc, +1,5,["as"]));return new f_b(new Z1b(d,a.f),c,b)}function H_(a,b){b.n==(Afc(),Jdc)&&E8b(b,(Hbc(),Fac))!=0&&F_(a,(IRb(),GQb),b);E8b(b,(Hbc(),Xac))!=0&&F_(a,(IRb(),$Qb),b);b.n==Jdc&&E8b(b,Gac)!=0&&F_(a,(IRb(),HQb),b);b.n==Jdc&&E8b(b,Gac)!=0&&E8b(b,Xac)!=0&&F_(a,(IRb(),IQb),b)}function Dlb(a,b,c,d,e,f){var g,h;Ld(e==(cmb(),_lb)||e==amb,e);h=new Ulb(b,Sd(c),Sd(d),a,e,f);g=Elb(a,b,c,d,Wlb,f+1);h.f=g;g.f=h;$tc(a.q,d,(Bn(),new hz(Qn(CE(xE(iW,1),mLc,1,5,[h,g])))));jDc(a.p,h);Hlb(a,h);jDc(a.p,g);Hlb(a,g)}function ptb(a, +b){var c,d,e;if(gqb(b)&&b.n!=(Afc(),eec)&&b.n!=(Afc(),gfc))for(d=b.c;d;d=d.f)if(hqb(b,d)&&d.n!=(Afc(),Vcc)){e=F9b((c=new J9b((Afc(),Vcc)),c),b);j9b(b,d,e);c9b(e,(Hbc(),Eac),1);d.n==tdc||u8b(e,d);d=e;stb(a)}}function BJb(a){zJb();var b,c,d,e;for(c=CE(xE(pR,1),sLc,106,0,[pJb,qJb,rJb,sJb,tJb,uJb,vJb,wJb,yJb,xJb]),d=0,e=c.length;d>16);b=d>>16&16;c=16-b;a=a>>b;d=a-256;b=d>>16&8;c+=b;a<<=b;d=a-4096;b=d>>16&4;c+=b;a<<=b;d=a-JLc;b=d>>16&2;c+=b;a<<=b;d=a>>14;b=d&~(d>>1);return c+ +2-b}}function xhb(a,b){var c,d;c9b(b,(Hbc(),gbc),0);c=b.Sh(false);while(b.c){while(!!(d=g9b(b))&&E8b(d,gbc)==0)q8b(c,d);if(c.c){whb(a,c);c=b.Sh(false)}if(d){Xd(E8b(d,gbc)!=0);r8b(d,jhb(a,g9b(d)));c9b(d,gbc,0);q8b(c,d)}}!!c.c&&whb(a,c)}function BTb(a){var b,c,d,e;e=DWb(a.g,0).d.b;iTb(a,(OSb(),hSb).b);c=!!mTb(a,(AZb(),bZb));d=jTb(a);return b=M$b(R$b(L$b(O$b(N$b(P$b(new T$b((Y$b(),V$b)),d),c),tTb(a)),pUb(a,1)),sTb(a)),new w$b(new Z1b(e,a.f))),I$b(b,new Z1b(e,a.f))}function j_(a,b){switch(b.n.f){case 76:case 100:case 88:Y_(a, +b.n,b);break;case 65:G_(a,b,true);break;case 96:r_(a,b,true),W_(b.c);break;case 154:z_(b);break;case 162:Z_(a,b,true);break;case 157:y0(a,b);break;case 109:B_(a,b,true)}}function Sdb(a){var b,c,d;Xd(a.n==(Afc(),Vec));for(d=(!a.c?(Cyc(),Cyc(),Byc):new Kbc(a.c)).Vd();d.Id();){c=d.Jd();Xd(c.n==Xec);Fyb();b=(ppb(),Yd(c.n==Xec,c),H8b(c,(n8b(),V7b)));if(!!b&&!kQb((iQb(),ZPb),b))return true}return false}function Chb(a,b,c,d){var e,f;f=F9b(Yjb(x3b(F9b(Yjb(K3b(OPc+(a.r.b==0?"":"$"+a.r.b)),a.g),b),F9b((n8b(), +new Wbc((Afc(),bfc),c)),b)),a.r.f.g?a.g.Wi(c):null),b);e=F9b(l3b(f,d),b);a.r.f.g&&r9b(e,f.d.Ii()?f.d.Ii().b.b:a.r.f.i);return e}function Fnb(a,b){var c;c=a.ei().ki();if(Wnc(c,Sd(b.r)))return Rnc(c,Sd(b.r));else if(Wnc(c,Sd(b.s)))return Rnc(c,Sd(b.s));else if(Wnc(c,Sd(b.c)))return Rnc(c,Sd(b.c));else if(Wnc(c,Sd(b.d)))return Rnc(c,Sd(b.d));return b.u[51]}function xTb(a,b){var c,d,e;e=DWb(a.g,0).d.b;d=KTb(a,b);if(AVb(a,(AZb(),OXb))&&!zVb(a,1,aZb)){c=(Bn(),new Zr);Ywc(c.a,Sd(d));while(AVb(a,OXb)&&!zVb(a, +1,aZb)){iTb(a,OXb);Xr(c,KTb(a,b))}return new ZZb(new Z1b(e,a.f),In(c.a))}return d}function L9b(a,b){n8b();Ld(!b.g,"new child has existing parent");Ld(!b.f,"new child has existing next sibling");Ld(!b.i,"new child has existing previous sibling");this.n=a;this.g=null;this.c=b;b.f=null;b.i=this.c;b.g=this;this.k=-1}function Pmb(a){var b,c,d,e,f,g,h;b=(Cyc(),new jzc(new xDc));Rc(b,a.a);h=new Ewc(a.a);while(h.b!=h.c){f=(e=vwc(h),e);g=f.a;for(d=new wxc(g);d.a0||(a.a.a&HPc)==$Lc||P4b(a.a)||B4b(a.a)>0||Q4b(a.a)||!!a.a.g||(a.a.a&HPc)==WPc||(a.a.a&HPc)==hMc||(a.a.a&HPc)==eQc)return false;else{l5b(a.a,b);a.e=true;return true}}function fhb(a,b){var c,d,e;Xd(E8b(b,(Hbc(),gbc))!=0); +if(b.n==(Afc(),zfc))return b;e=H3b(b);c=j3b(e?(Xd(H3b(b)),new L9b(Uec,b)):b);epb(a.f.b,b,new Cib(a.f));epb(a.f.b,c,new Eib);d=z8b(c.c?c.c.i:null);thb(a,c,null,null);return e?g9b(d):d}function Bqb(a){ppb();var b;if(a.n==(Afc(),Jdc)){b=a.g;switch(b.n.f){case 86:case 87:case 98:return true;case 116:return(b.c?b.c.i:null)==a&&(E8b(b,(n8b(),N7b))!=0||E8b(b,M7b)!=0||E8b(b,O7b)!=0);default:return false}}else return false}function nNb(a,b){var c;if(b==(fOb(),WNb)){xNb(a);c=lNb(a,!a.o?mOb(a.j):GMb(a));if(c){xNb(a); +OMb(a,aOb)?!a.o?mOb(a.j):GMb(a):(CMb(a,YRc,a.j.g,lOb(a.j)),null)}return c}else{CMb(a,"msg.jsdoc.missing.braces",a.j.g,lOb(a.j));return mNb(a,b)}}function uv(a,b){var c,d,e,f;f=yZ(rZ(VLc,Vrc(yZ(rZ(b==null?0:Db(b),WLc)),15)));c=f&a.b.length-1;e=null;for(d=a.b[c];d;e=d,d=d.a)if(d.d==f&&Gd(d.i,b)){!e?a.b[c]=d.a:e.a=d.a;ov(d.c,d.f);nv(d.b,d.e);--a.f;++a.e;return true}return false}function y8(a){!a.c&&(a.c=w8(a));if(a.f.a.length!=0||a.e.a.length!=0){a.c=YDb($Db(ZDb(_Db(eEb(dEb(cEb(NDb(a.i.a,a.i.a),J8(a.c.f, +a.e)),J8(a.c.g,a.f)),a.c.i),a.c.c),a.c.a),a.c.b));a.f.a=zE(iW,mLc,1,0,5,1);a.e.a=zE(iW,mLc,1,0,5,1)}return a.c}function Kgb(a,b,c,d){var e,f,g,h;f=(h=(ae(Hsc(d,_sc(46))==-1,HMc,d),n8b(),new Wbc((Afc(),rec),d)),h);q9b(f,H8b(c,(Hbc(),mbc)));j9b(c.g,c,f);e=q3b(c,(g=(ae(Hsc(d,_sc(46))==-1,HMc,d),new Wbc(rec,d)),g),ufc);o8b(a.c?a.c.i:null,e,b);return e}function Qwb(b){var c,d;if(b.d!=null)return;try{d=Rsc(b.a,gMc,-1);b.d=zE(yF,iMc,46,d.length,15,1);for(c=1;c0||(a.a.a&HPc)==$Lc||P4b(a.a)||B4b(a.a)>0||Q4b(a.a)||!!a.a.g||(a.a.a&HPc)==WPc||(a.a.a&HPc)==hMc||(a.a.a&HPc)==eQc)&&n4b(a.a,b)){a.e=true;return true}return false}function vdb(a,b){var c,d,e,f,g;Ld(V8b(a),a);Ld(b.n== +(Afc(),Ycc),b);c=g9b(b);g=G9b(O$(x8b(a,false),ePc),c);d=F9b((f=Tqb(g,CE(xE(HU,1),IMc,7,0,[])),f),b);q8b(d,F9b((e=new J9b(lfc),e),c));c9b(d,(n8b(),W7b),0);while(b.c)q8b(d,g9b(b));return d}function dob(a,b,c){var d,e,f,g;Yd((ppb(),ypb(c)||c.n==(Afc(),Jdc)||c.n==(Afc(),pec)||c.n==(Afc(),Vec)&&!c.g||c.n==(Afc(),Xec)),c);for(g=(e=(new fvc(Kob(b).e)).a.He().Vd(),new kvc(e));g.a.Id();){f=(d=g.a.Jd(),d.We());twc(a.b).Ch(f.b,false)}}function V5b(a,b){if(!a&&!b)return true;if(!a&&!!b||!!a&&!b)return false; +if(a.d==null&&b.d!=null||a.d!=null&&b.d==null)return false;return(a.d==null&&b.d==null||a.d.Xh(b.d,false,true,false,false))&&a.f==b.f&&a.e==b.e&&a.c==b.c&&a.b==b.b}function SB(a,b){var c,d,e;TB(a);switch(b.f){case 7:UB(a>0&(a&a-1)==0);case 1:case 3:return 31-Trc(a);case 0:case 2:return 32-Trc(a-1);case 5:case 4:case 6:d=Trc(a);c=-1257966797>>>d;e=31-d;return e+(~~(c-a)>>>31);default:throw gZ(new Jqc);}}function x0(a,b){var c,d,e;b0((Afc(),ofc),b);d=C8b(b);(d<2||d>3)&&D0(ZMc+d,b);n_(a,b.c);e=false; +c=b.c.f;b0(Vcc,c);T_(c);if(c.c){p_(a,c.c);e=true}if(C8b(b)==3){n_(a,b.c?b.c.i:null);e=true}e||D0("Missing catch or finally for try statement.",b)}function Rwb(b,c){var d,e;Qwb(b);if(c>b.d.length)return null;c<1&&(c=1);e=b.d[c-1];d="";try{d=b.a}catch(a){a=fZ(a);if(pF(a,104))return null;else throw gZ(a);}return Isc(d,_sc(10),e)==-1?e>=d.length?null:d.substr(e):Vsc(d,e,Isc(d,_sc(10),e))}function CKb(a,b){var c,d,e;e=new Cwc;while(b){GKb(a,b);DKb(a,b);HKb(a,b);FKb(a,b);EKb(a,b);b.n==(Afc(),Jdc)&&b.g.n== +Vcc&&(!b.g?null:b.g.g).n!=Jdc&&cKb(a,b,(IRb(),KQb));d=b.f;c=b.c;if(c){!!d&&kwc(e,d);b=c}else d?b=d:b=vwc(e)}Xd(e.b==e.c)}function ZE(a,b){var c,d,e,f,g;b&=63;c=a.h;d=(c&uMc)!=0;d&&(c|=-1048576);if(b<22){g=c>>b;f=a.m>>b|c<<22-b;e=a.l>>b|a.m<<22-b}else if(b<44){g=d?tMc:0;f=c>>b-22;e=a.m>>b-22|c<<44-b}else{g=d?tMc:0;f=d?sMc:0;e=c>>b-44}return HE(e&sMc,f&sMc,g&tMc)}function oob(a,b,c,d){mob(this);Yd((ppb(),ypb(a)||a.n==(Afc(),Jdc)||a.n==(Afc(),pec)||a.n==(Afc(),Vec)&&!a.g),a);a.n==(Afc(),Jdc)&&Yd(!c, +a);this.i=a;this.e=false;this.g=b;if(c){Yd(!ypb(a),a);this.f=this}else{Yd(ypb(a)||a.n==Jdc,a);this.f=d.f}}function dqb(a){var b;if(!(a.n==(Afc(),Jdc)&&iqb(a.g)&&a.n==Jdc&&Gqb(a.c)))return false;b=a.g;while(b)switch(b.n.f){case 82:return b.g.n!=Jdc;case 65:case 85:case 158:case 109:case 112:return false;default:Yd(b.n==eec,b);b=b.g}return false}function ITb(a){var b,c,d;c=DWb(a.g,0).d.b;d=TUb(a);while(b=DWb(a.g,0).d.b.b>a.f.b,!(b||zVb(a,0,(AZb(),ZYb))||zVb(a,0,(AZb(),KXb))||zVb(a,0,(AZb(),XXb)))&& +zVb(a,0,(AZb(),HYb))){iTb(a,(AZb(),HYb));iTb(a,MXb);d=new OZb(new Z1b(c,a.f),d)}return d}function aTb(a){var b,c,d,e;d=new fxc;Ywc(d,Yrc(0));for(c=0;cE8b(c,Hac)){if(K1(b,c))throw gZ(new Irc(a+"unchanged scope marked as changed: "+B1(b)));}else if(!K1(b, +c))throw gZ(new Irc(a+"changed scope not marked as changed: "+B1(b)));}function bgb(a){var b,c,d,e;c=H8b(a,(Hbc(),mbc));if(c){b=a.n==(Afc(),Qdc)||E8b(a,(n8b(),M7b))!=0;if(b&&!!((HPc&c.a)==$Lc?c.i:null))return(HPc&c.a)==$Lc?c.i:null;else{d=G4b(c);if(d.Yd()==1){e=H4b(c,tu(G4b(c).Vd()));if(e)return e}}}return null}function JLb(a,b){var c;c=yKb(a.a,b.b);if(c.n==(Afc(),Jdc)||c.n==bdc||c.n==jec||c.n==gdc){a.a.d.Ah("Lexical declarations are only allowed at top level or inside a block.",a.a.q,(RJb(),b.o.b.b+ +1),b.o.b.a);return c}return fKb(a.a,eec,mMb(a,b.a),c)}function y_(a,b,c){var d,e;e=C8b(c);(e<1||e>2)&&D0(VMc+e,c);d=c.c;switch(d.n.f){case 93:l_(a,b,d);break;case 94:h0(a,b,d);break;default:D0("Invalid destructuring lhs first child for "+b+TMc,c)}!!c.c&&!!c.c.f&&c.c.f==(c.c?c.c.i:null)&&E_(a,c.c.f)}function E3(a){var b,c,d;c=null;if(a.n==(Afc(),Pdc))c=(a.c?a.c.i:null).Wh();else if(a.n==rec){d=a.Wh();b=Jsc(d,_sc(36));b!=-1&&(c=d.substr(b+1))}if(c!=null)if(Esc(c,"inherits"))return a6(),$5;else if(Esc(c, +"mixin"))return a6(),_5;return null}function Hob(){Hob=JZ;Gob=new Mcb("JSC_UNTRANSPILABLE",(E2(),B2),new Rtc('Cannot convert {0} feature "{1}" to targeted output language.'));Eob=rQb((iQb(),TPb),CE(xE(FR,1),sLc,24,0,[(IRb(),rRb),uRb,vRb,wRb]));Fob=oQb(TPb,HRb);mQb(mQb(TPb,Eob),Fob)}function YKb(a,b,c,d){var e,f;e=d.n==(Afc(),_cc)?d.c:d;X8b(e)||a.a.d.Ah(x2b("Invalid %s %s operand.",CE(xE(iW,1),mLc,1,5,[c?"postfix":"prefix",b==$dc?"increment":"decrement"])),a.a.q,T9b(d.k),S9b(d.k));f=eKb(a.a,b,d);b9b(f, +(n8b(),_7b),c);return f}function ZWb(a){yWb(a);while(xWb(a,a.f)&&(xWb(a,a.f)?zsc(a.b,a.f):0)!=93&&!jXb(xWb(a,a.f)?zsc(a.b,a.f):0))if(!$Wb(a))return false;if((xWb(a,a.f)?zsc(a.b,a.f):0)!=93){FWb(a,"']' expected",CE(xE(iW,1),mLc,1,5,[]));return false}yWb(a);return true}function kkb(a,b,c){var d,e;if(b.n==(Afc(),_ec)){e=(Xd(!!b.c&&!b.c.f),b.c);if(e.n==rec&&nkb(a,e.Wh()))return false}if(c){d=b.g;if(Eqb(d)&&b==d.c&&b.c.n==rec&&nkb(a,b.c.Wh()))return false;return tpb(b,a.c,a.e)}else return ppb(),upb(b, +false,a.b)}function XSb(){OSb();return CE(xE(IR,1),sLc,37,0,[RRb,SRb,TRb,WRb,XRb,ZRb,$Rb,_Rb,aSb,fSb,gSb,hSb,iSb,lSb,mSb,tSb,zSb,CSb,DSb,ESb,GSb,ISb,JSb,KSb,LSb,MSb,URb,VRb,bSb,cSb,dSb,kSb,BSb,jSb,nSb,qSb,vSb,wSb,xSb,ySb,ASb,NSb,uSb,FSb,eSb,YRb,HSb,rSb,sSb])}function gB(a,b){var c,d;if(b>127)throw gZ(new QB((c=b>>>0,dMc+c.toString(16))));d=a.e[b];if(d==-1)if(b<=32||b==127)throw gZ(new QB((c=b>>>0,dMc+c.toString(16))));else throw gZ(new QB("Unrecognized character: "+String.fromCharCode(b)));return d} +function kib(a,b){this.r=a;this.n=new fDc;this.c=new Cwc;this.i=new Cwc;this.e=new Cwc;this.k=new Cwc;this.f=new mDc;this.a=new fxc;this.q=new fxc;this.p=new pib(this);Xd(this.p.c==0);this.j=new pib(this);Xd(this.j.c==1);Ywc(this.a,this.j);this.g=b}function lvb(a,b,c){var d,e,f,g,h;for(e=(!c.c?(Cyc(),Cyc(),Byc):new Kbc(c.c)).Vd();e.Id();){d=e.Jd();h=d.c;Xd(d.n==(Afc(),cfc)&&(h.n==bfc||h.n==Edc));f=d.Wh();Esc(f.substr(0,2),rOc)&&(f=f.substr(2));g=h.n==bfc?b+(""+h.Wh()):UQc;_tc(a.b,b+(""+f),g)}}function F6b(a, +b){if(!!b&&!((a.a.a&2)!=0||W4b(a.a)||(a.a.a&tQc)!=0||F4b(a.a)>0||(a.a.a&HPc)==$Lc||P4b(a.a)||B4b(a.a)>0||Q4b(a.a)||!!a.a.g||(a.a.a&HPc)==WPc||(a.a.a&HPc)==hMc||(a.a.a&HPc)==eQc)){x5b(a.a,b,eQc);a.e=true;return true}else return false}function s7b(a,b){if(!!b&&!((a.a.a&2)!=0||W4b(a.a)||(a.a.a&tQc)!=0||F4b(a.a)>0||(a.a.a&HPc)==$Lc||P4b(a.a)||B4b(a.a)>0||Q4b(a.a)||!!a.a.g||(a.a.a&HPc)==WPc||(a.a.a&HPc)==hMc||(a.a.a&HPc)==eQc)){x5b(a.a,b,WPc);a.e=true;return true}else return false}function cmb(){cmb=JZ; +_lb=new dmb("SET_FROM_GLOBAL",0);amb=new dmb("SET_FROM_LOCAL",1);$lb=new dmb("PROTOTYPE_GET",2);Wlb=new dmb("ALIASING_GET",3);Zlb=new dmb("DIRECT_GET",4);Xlb=new dmb("CALL_GET",5);Ylb=new dmb("DELETE_PROP",6);bmb=new dmb(kQc,7)}function fMb(a,b){var c,d,e,f,g;g=eKb(a.a,(Afc(),ofc),zKb(a.a,b.a));c=dKb(a.a,Vcc);f=false;d=b.b;if(d){T9b(c.k)==-1&&vKb(c,d.o.b,d.o.a);f=true;q8b(c,yKb(a.a,d))}q8b(g,c);e=b.c;!!e&&q8b(g,zKb(a.a,e));!f&&!!e&&T9b(c.k)==-1&&vKb(c,e.o.b,e.o.a);return g}function XWb(a){if(!lXb(xWb(a, +a.f)?zsc(a.b,a.f):0)){FWb(a,"Expected regular expression first char",CE(xE(iW,1),mLc,1,5,[]));return false}if(!YWb(a))return false;while(xWb(a,a.f)&&kXb(xWb(a,a.f)?zsc(a.b,a.f):0))if(!YWb(a))return false;return true}function Mrb(a,b){var c,d,e;Kd(b.n==(Afc(),eec));e=b.c?b.c.i:null;switch(e.n.f){case 83:case 82:case 72:case 73:case 101:case 102:case 70:case 71:return;default:d=(c=new J9b(Vcc),c);H9b(d,e);j9b(b,e,d);r8b(d,e);Nrb(a,"LABEL normalization",b);return}}function pNb(a,b){var c,d;d=fNb(a,b); +if(OMb(a,(fOb(),VNb))){!a.o?mOb(a.j):GMb(a);xNb(a);c=oNb(a,d.Wh(),!a.o?mOb(a.j):GMb(a));if(c){r8b(d,c);xNb(a);if(!OMb(a,_Nb))return CMb(a,"msg.jsdoc.missing.gt",a.j.g,lOb(a.j)),null;!a.o?mOb(a.j):GMb(a)}}return d}function CNb(a){xMb();var b,c;b=_Ib(gJb(dJb(eJb(bJb(hJb(aJb(fJb(cJb(gJb(dJb(new iJb,(zJb(),xJb)),(KJb(),JJb)),(mJb(),lJb)),(FJb(),EJb)),WIb((bn(),mz(),lz))),(null,lz)),(null,lz)),false),pJb),IJb));c=new BNb(new rOb(a),a,0,null,b,(a3b(),_2b));return c}function $5b(a,b){if(!a&&!b)return true; +if(!a&&!!b||!!a&&!b)return false;if(a.d==null&&b.d!=null||a.d!=null&&b.d==null)return false;return(a.d==null&&b.d==null||a.d.Xh(b.d,false,true,false,false))&&a.f==b.f&&a.e==b.e&&a.c==b.c&&a.b==b.b&&a.a==b.a}function b3(){b3=JZ;a3=new Mcb("JSC_TYPE_IMPORT_CODE_REFERENCE",(E2(),B2),new Rtc("Cannot reference goog.requireType()''d name {0} outside of a type annotation."));x3b((ae(Hsc(kNc,_sc(46))==-1,HMc,kNc),n8b(),new Wbc((Afc(),rec),kNc)),new Wbc(bfc,pNc))}function _hb(a,b,c,d){var e;if(swc(a.k)||qwc(a.k).c< +c.c)e=PPc;else{Xd(qwc(a.k).c!=c.c);e="jumpThroughFinallyBlocks"}if(d==0){p8b(b.g,Dhb(a,b,e,CE(xE(HU,1),IMc,7,0,[mib(c,b)])),b);k9b(b,Fhb(a,b))}else k9b(b,aib(a,b,e,CE(xE(HU,1),IMc,7,0,[mib(c,b)])))}function Sjb(a,b){Rjb();var c,d,e;e=(!a.P&&(a.P=new llc),a.P);c=e.u[0];d=r9b((C6(a,APc+Xsc("arrayFromIterable",(bFc(),_Ec)),false),l3b(Wqb(a,"$jscomp.arrayFromIterable"),CE(xE(HU,1),IMc,7,0,[b]))),c);r9b(d.c,Okc(e,c,CE(xE(cV,1),xPc,23,0,[])));return d}function bqb(a){ppb();switch(a.f){case 61:case 62:case 1:case 2:case 3:case 4:case 5:case 36:case 37:case 6:case 8:case 7:case 9:case 41:case 40:case 10:case 11:case 12:case 13:case 14:case 15:case 16:case 17:case 18:return true; +default:return false}}function Dub(a,b){var c,d,e,f,g,h;g=x8b(b.a,false);if(a.f.d){h=(f=new J9b((Afc(),lfc)),f);d=$$(h);c=g.c;E8b(a.a.b.g,(Hbc(),zbc))!=0?j9b(c.g,c,d):k9b(c,$$(d))}return e=g3b((ae(Hsc("",_sc(46))==-1,HMc,""),n8b(),new Wbc((Afc(),rec),"")),new J9b(Jec),g),e}function aGb(a,b,c){var d,e,f;f="/".length;Esc(c.substr(c.length-f,f),"/")&&(c=Vsc(c,0,c.length-1));for(d=0;d0){e.a+=".";f=zE(xF,eLc,46,a.b.n,15,1);Lxc(f,f.length,48);e.a+=gtc(f,0,f.length)}e.a+="E+00";c=NZ(a.a);ptc(b,Nsc(c,69,101));if(a.b.i&&a.b.n==0){d=b.a.indexOf("e");b.a=Vsc(b.a,0,d)+"."+Usc(b.a,d)}}function n2(){n2= +JZ;m2=new Mcb("JSC_UNDEFINED_NAME",(E2(),D2),new Rtc("{0} is never defined"));new Mcb("JSC_NAME_DEFINED_LATE",D2,new Rtc("{0} defined before its owner. {1} is defined at {2}:{3}"));l2=new Mcb("JSC_STRICT_MODULE_DEP_QNAME",C2,new Rtc(sNc))}function m7(a,b){var c,d,e,f;c=new fxc;for(f=new Tu(wu($t(b.a),new Xt));Ru(f);){d=Su(f);(!E0(a.G.Z)||true||Esc(kOc,y8(d).c.Ie(mNc)))&&(c.a[c.a.length]=d,true)}for(e=new wxc(c);e.a=a.c.a.length)return null;Xd(b>=0);Xd(c>=0);if($wc(a.c,b)==null)return dC(a,b);d=$wc(a.c,b);Xd(d.a.length!=0);if(d.a[0].$f()>c)return dC(a,b);e=jC(d,c,0,d.a.length-1);Zd(e>=0,"unexpected:%s",e);return cC(a,d.a[e])} +function O6(a){var b,c,d,e,f;f=new mDc;if(a.v)for(e=v8b(a.v).Vd();e.Id();){d=e.Jd();E8b(d,(n8b(),l8b))!=0&&jDc(f,Q6(a,(b=H8b(d,(Hbc(),ybc)),!b?null:b.ug())))}return new qub((c=!a.g?(Bn(),Bn(),An):t1(a.g.a,(E2(),B2)),!a.g?(Bn(),Bn(),An):t1(a.g.a,(E2(),D2)),c))}function EGb(){EGb=JZ;new Mcb("JSC_BAD_IJS_FILE_NAME",(E2(),B2),new Rtc("--ijs file name must end in .i.js: {0}"));DGb=new Mcb("JSC_CONFLICTING_IJS_FILE",D2,new Rtc("Found --ijs file with the same basename as --js or --weakdep file.\nFile 1: {0}\nFile 2: {1}"))} +function ioc(a,b){var c;Sd(b);if(!(!!b.Li()&&Zfc(a.e,b.Li().e))){if(!b.Li())if(Igc(a,b,new Akc(true),0))return a;else if(b.zi(a))return igc(b);if(b.xi())return a.A.u[59];return a.A.u[58]}c=b.Li();Sd(c);if(Onc(a.B,c.B,2,new ukc(true),0))return a;return a.d}function E4(a,b,c){var d,e,f,g,h,i;h=0;d=0;for(f=0;f=0?a.c=new gxc:a.c=new fxc;xg(a.a,b.a);lC(new qC(a,b.d))}function y3(){y3=JZ;new Mcb("JSC_REFLECT_OBJECTLIT_EXPECTED",(E2(),D2),new Rtc("Object literal expected as second argument"));y3b((ae(Hsc(kNc,_sc(46))==-1,HMc,kNc),n8b(),new Wbc((Afc(),rec),kNc)),new Wbc(bfc, +"reflect"),CE(xE(HU,1),IMc,7,0,[new Wbc(bfc,"cache")]))}function xlb(a){var b,c,d;d=Tpb(a);if(!d)return false;else if(d.n==(Afc(),bdc))return true;b=(ppb(),c=Dpb(a),!c?null:H8b(c,(Hbc(),mbc)));return!!b&&((b.a&2)!=0&&d.n==(Afc(),Jdc)||((b.a&512)!=0||(b.a&bPc)!=0)&&d.n==(Afc(),Jdc)||(b.a&HPc)==eQc&&d.n==(Afc(),Eec))}function VTb(a){var b,c,d,e;e=DWb(a.g,0).d.b;iTb(a,(AZb(),HXb));d=new w$b(pTb(a,DWb(a.g,0).d.b));if(DWb(a.g,0).e==GYb){iTb(a,GYb);zVb(a,0,HYb)||zVb(a,0,FYb)?d=UUb(a,0):d=wUb(a);iTb(a,LXb)}else RVb(a, +(IRb(),pRb));c=STb(a);b=new XZb(new Z1b(e,a.f),d,c);return b}function J3b(a){switch(a.n.f){case 80:case 65:return true;case 82:case 74:case 96:case 88:case 75:case 89:case 71:case 84:case 72:case 73:case 101:case 102:case 66:case 83:case 100:case 67:case 39:case 44:case 76:case 70:case 77:return true;default:return false}}function oNb(a,b,c){var d,e,f,g;f=lNb(a,c);if(!f)return null;g=(d=new J9b((Afc(),Vcc)),d);e=1;q8b(g,f);while(OMb(a,(fOb(),PNb))){!a.o?mOb(a.j):GMb(a);xNb(a);f=lNb(a,!a.o?mOb(a.j): +GMb(a));if(!f)return null;++e;q8b(g,f)}Esc(b,NMc)&&e==1&&r8b(g,QMb(a,Pec));return g}function QUb(a,b){var c,d;d=DWb(a.g,0).d.b;c=(Bn(),new Zr);iTb(a,(AZb(),FYb));while(KVb(a,0)||DWb(a.g,0).e==HYb){Xr(c,RUb(a,b));if(AVb(a,OXb))iTb(a,OXb);else break}if(AVb(a,aZb)){RVb(a,(IRb(),nRb));Xr(c,XUb(a,b))}iTb(a,KXb);return new u_b(new Z1b(d,a.f),In(c.a))}function P9b(a,b,c,d,e){n8b();Kd(!b.g);Kd(!b.f);Kd(!b.i);Kd(!c.g);Kd(!c.f);Kd(!c.i);Kd(!d.g);Kd(!d.f);Kd(!d.i);Kd(!e.g);Kd(!e.f);Kd(!e.i);this.n=a;this.g= +null;this.c=b;b.f=c;b.i=e;b.g=this;c.f=d;c.i=b;c.g=this;d.f=e;d.i=c;d.g=this;e.f=null;e.i=d;e.g=this;this.k=-1}function Kjc(a,b,c){var d,e,f,g,h;e=null;if(chc(a.b,b.b,0,new ukc(true)))e=a.b.a;else return null;f=c?a.b.b.ii(b.b.b):a.b.b.hi(b.b.b);h=null;if(ugc(a.k,b.k))h=a.k;else{d=c?a.k.ii(b.k):a.k.hi(b.k);h=d}g=a.b.c||b.b.c;return uic(Kic(Eic(Bic(new Lic(a.A),e),f,g),h))}function Zib(a,b,c,d){var e,f,g,h,i;h=c.c?c.c.i:null;g=C3b(new J9b((Afc(),tdc)),new J9b(tdc),h.Sh(false));F9b(g,c);p8b(d,g,c);Wib(a, +b,g,d);i=Sib(a,h.Wh());for(f=v8b(c.c).Vd();f.Id();){e=f.Jd();qGc(a.b,(e.c?e.c.i:null).Wh(),new Mib(i+"."+e.c.Wh(),e))}e9b(d,c);Vob(b)}function jPb(a,b){var c,d;if(!JOb(a,b,(NPb(),LPb)))return false;d=(Pd(b.n==(Afc(),Ycc),iSc,b),C8b(b)-1);for(c=0;c=1){b=c.a.ce(0);b.p==(n1b(),Q0b)&&TVb(a,b,"Setter must not have a rest parameter",CE(xE(iW,1),mLc,1,5,[]))}return c}function S5b(a,b,c,d,e){if(b>d)throw gZ(new Irc("Recorded bad position information\nstart-line: "+b+gMc+"end-line: "+d));else if(b==d&&c>=e)throw gZ(new Irc("Recorded bad position information\nline: "+b+gMc+ +"start-char: "+c+gMc+"end-char: "+e));a.f=b;a.e=c;a.c=d;a.b=e}function Kkc(){return CE(xE($U,1),iMc,46,15,[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77])}function UGb(){UGb=JZ;SGb=new Mcb("JSC_DUPLICATE_IMPORT",(E2(),D2),new Rtc('The module "{0}" has already been imported at {1}, {2}.'));TGb=new Mcb("JSC_DEFAULT_EXPORT", +D2,new Rtc("Do not use the default export. There is no way to force consistent naming when imported."))}function d5(a,b,c,d,e,f,g){var h,i,j;Xd(!!c.cd);j=b==0?new u5(f,c.rc,c.Vb,c.cd):new F5(c.Vb,c.cd);h=b==2?new $Bb(j,g.a.c,g.a.e):new M4(j,g.a.c);d&&L3(h.c,"/** @fileoverview @typeSummary */\n");e&&(L3(h.c,"'use strict';"),h.c.Ug());h.fh(a,(_4(),W4));j.Sg();i=j.j.a;return i}function qlb(a,b){var c,d,e;if(!b)return Rlb(),Plb;switch(b.n.f){case 96:return Rlb(),Llb;case 43:return Rlb(),Olb;case 65:return Rlb(), +Mlb;case 61:return qlb(a,b.c?b.c.i:null);case 60:c=b.c.f;d=qlb(a,c);if(d!=(Rlb(),Plb))return d;e=c.f;return qlb(a,e)}return Rlb(),Plb}function hEb(a,b,c){var d,e,f,g,h;d=gEb(a,b,c);f=d;ZEb();!Esc(d.substr(0,"/".length),"/")&&!(Esc(d.substr(0,rOc.length),rOc)||Esc(d.substr(0,nRc.length),nRc))&&(f="/"+d);if(a.e.Sd(f))return d;for(h=a.f.Vd();h.Id();){g=h.Jd();e=g+(""+f);if(a.e.Sd(e))return d}return null}function xHb(){xHb=JZ;vHb=new Mcb("JSC_PRIMITIVE_OBJECT",(E2(),D2),new Rtc("Explicit creation of a {0} object.")); +wHb=new Mcb("JSC_PRIMITIVE_OBJECT_DECLARATION",D2,new Rtc("Declaration of {0} object instead of primitive type."));bn();pp(CE(xE(iW,1),mLc,1,5,[lPc,oPc,oMc]))}function gNb(a,b){var c,d;c=false;if(b==(fOb(),QNb)){b=!a.o?mOb(a.j):GMb(a);if(b==aOb){a.o=b;return zNb(a,(Afc(),sdc),new J9b(tdc))}c=true}d=lNb(a,b);if(d){xNb(a);if(c)d=zNb(a,(Afc(),sdc),d);else if(OMb(a,UNb)){!a.o?mOb(a.j):GMb(a);xNb(a);d=zNb(a,(Afc(),ydc),d)}}return d}function K_(a,b){var c,d,e;F_(a,(IRb(),iRb),b);b0((Afc(),Vdc),b);d=Cfc(b.n); +d!=-1&&(c=C8b(b),d!=c&&D0(QMc+d+RMc+c,b));b.c.n==rec?W_(b.c):b0(tdc,b.c);e=b.c.f;switch(e.n.f){case 106:M_(e);break;case 108:c0(e)&&e.Wh().length==0&&D0(WMc,e);break;default:b0(tdc,e)}s0(B8b(b,2))}function lMb(a,b){var c,d,e,f;switch(b.a.f){case 30:bKb(a.a,b,(IRb(),RQb));e=(Afc(),gdc);break;case 38:bKb(a.a,b,(IRb(),fRb));e=(Afc(),jec);break;case 25:e=(Afc(),ufc);break;default:throw gZ(new Hrc);}f=dKb(a.a,e);for(d=b.b.Vd();d.Id();){c=d.Jd();q8b(f,AKb(a.a,c))}return f}function GVb(b){var c;if(b.b.d&& +AVb(b,(AZb(),GYb))||AVb(b,(AZb(),EYb))){c=new ZVb(b.b,new S1b,b.g.i,uWb(b.g),uwc(b.e).b);try{tTb(c);pUb(c,2);AVb(c,(AZb(),NXb))&&(iTb(c,NXb),nVb(c));return AVb(c,zXb)}catch(a){a=fZ(a);if(pF(a,425))return false;else throw gZ(a);}}return false}function chc(a,b,c,d){var e,f,g,h;g=a.a.c;e=b.a.c;while(!!g&&!!e){h=g.d;f=e.d;if(h){if(!!f&&!Qfc(h,f,c,d))return false}else if(f)return false;if(E8b(g,(Hbc(),qbc))!=0!=(E8b(e,qbc)!=0))return false;if(E8b(g,Ebc)!=0!=(E8b(e,Ebc)!=0))return false;g=g.f;e=e.f}return g== +e}function sjc(a){var b,c,d,e,f,g,h;e=a.B;g=(ae(a.oi()||a.f==(dkc(),bkc),lRc,a),cgc(a.k)).ki();if(e==g)return Bn(),Bn(),An;d=(Bn(),new Zr);f=(h=g.c.wf(g.d.Yd(),g.c.Yd()),mp(h));for(c=e.c.wf(e.d.Yd(),e.c.Yd()).Vd();c.Id();){b=c.Jd();f.Sd(b)||(Ywc(d.a,Sd(b)),d)}return In(d.a)}function lbb(a,b){var c,d,e,f,g,h,i;j:for(i=b.b.Vd();i.Id();){h=i.Jd();g=(Wbb(),gFc(Vbb,h,new ecb),Xtc(Vbb,h));for(f=(d=(new Yvc(a.c.a)).a.He().b.vj(),new ewc(d));f.a.Id();){e=(c=f.a.Jd(),c.Ve());if(e.th(g))continue j;else if(e.uh(g))return false}return false}return true} +function oUb(a,b,c){var d,e,f;!c&&(c=new s_b(pTb(a,DWb(a.g,0).d.b)));iTb(a,(AZb(),ZYb));AVb(a,ZYb)?e=new s_b(pTb(a,DWb(a.g,0).d.b)):e=xTb(a,1);iTb(a,ZYb);AVb(a,LXb)?f=new s_b(pTb(a,DWb(a.g,0).d.b)):f=xTb(a,1);iTb(a,LXb);d=hVb(a);return new F$b(new Z1b(b,a.f),c,e,f,d)}function wmb(){wmb=JZ;vmb=new Mcb("JSC_J2CL_REFERENCE_EQUALITY",(E2(),D2),new Rtc("Reference equality may not be used with the specified type: {0}"));oo();new kz(CE(xE(QX,1),PLc,50,0,[(el(mQc,nQc),new Wq(mQc,nQc)),(el(oQc,pQc),new Wq(oQc, +pQc)),(el(qQc,rQc),new Wq(qQc,rQc))]))}function XOb(a,b){if(!JOb(a,b,(NPb(),BPb)))return false;if((Pd(b.n==(Afc(),Ycc),iSc,b),B8b(b,1)).n!=bfc){a.a.Bh(URc+occ(jSc,CE(xE(iW,1),mLc,1,5,[tPc])),a.b.ug(),a.d,a.c);mPb(a,BPb.d);return false}if(!gPb(a,(Pd(b.n==Ycc,iSc,b),B8b(b,2)))){mPb(a,BPb.d);return false}return true}function Prb(a,b){var c,d,e,f;for(c=b.c;c;c=f){f=c.f;ppb();if(!!c&&(c.n==(Afc(),ufc)||c.n==jec||c.n==gdc)){if(a.a&&!c.c)throw gZ(new Irc("Empty VAR node."));while(c.c!=(c.c?c.c.i:null)){d= +c.c;e9b(c,d);e=F9b(new L9b(c.n,d),b);p8b(b,e,c);Nrb(a,"VAR with multiple children",b)}}}}function TKb(a,b,c,d,e){var j;RJb();var f,g,h,i;h=new IKb(c,b,d,e,a.a);i=iLb(h.t,a);T9b(i.k)==-1&&vKb(i,a.o.b,a.o.a);if(a.a)for(g=a.a.Vd();g.Id();){f=g.Jd();(f.b==(e$b(),b$b)||f.b==a$b)&&!kDc(h.k,f)&&(j=TJb(h,f),jDc(h.k,f),ZJb(h,j),undefined)}rKb(h,i);CKb(h,i);h.o=i;return h}function UKb(a){RJb();switch(a.f){case 76:return Afc(),zec;case 77:return Afc(),Scc;case 63:return Afc(),Oec;case 64:return Afc(),wec;case 9:return Afc(), +ndc;case 24:return Afc(),pfc;case 26:return Afc(),vfc;default:throw gZ(new Irc(!a?jLc:a.a==null?(OSb(),Po(pSb,a)).c:a.a));}}function VE(a){var b,c,d,e,f;if(isNaN(a))return iF(),hF;if(a<-9223372036854775808)return iF(),fF;if(a>=0x7fffffffffffffff)return iF(),eF;e=false;if(a<0){e=true;a=-a}d=0;if(a>=wMc){d=vF(a/wMc);a-=d*wMc}c=0;if(a>=vMc){c=vF(a/vMc);a-=c*vMc}b=vF(a);f=HE(b,c,d);e&&NE(f);return f}function kjb(a,b){var c,d,e,f;Kd(b.n==(Afc(),zcc));e=hjb(a,b);e.a[0].n==zcc?c=axc(e,0):c=r9b(f3b(CE(xE(HU, +1),IMc,7,0,[])),a.a);if(e.a.length==0)f=c;else{d=r9b(x3b(c,(n8b(),new Wbc(bfc,VPc))),a.d);f=l3b(d,exc(e,zE(HU,IMc,7,0,0,1)))}I9b(f,b);r9b(f,a.a);j9b(b.g,b,f);w7(a.c,f)}function oLb(a,b){var c,d,e,f;f=null;c=null;e=null;while(b){(b.b.e==(AZb(),dZb)||b.b.e==eZb)&&bKb(a.a,b,(IRb(),WQb));e=c;c=eKb(a.a,RKb(b.b.e),yKb(a.a,b.c));T9b(c.k)==-1&&vKb(c,b.o.b,b.o.a);!!e&&r8b(e,c);if(pF(b.a,98))b=b.a;else{d=yKb(a.a,b.a);r8b(c,d);b=null}!f&&(f=c)}return f}function vlc(a,b){var c,d,e,f,g,h,i,j;h=false;j=new fxc; +for(f=(Yoc(new opc,b.b)&&Voc(b),b.a).Vd();f.Id();){e=f.Jd();i=e.Ni(a);i!=e&&(h=true);j.a[j.a.length]=i}if(h){g=new $pc(a.e,false);for(d=new wxc(j);d.a>>0,d.toString(16));LIc(RIc("com.google.common.base.Strings"), +(bIc(),aIc),"Exception during lenientFormat for "+e,c);return"<"+e+" threw "+frc(c.Fj)+">"}else throw gZ(a);}}function vD(a,b){var c;if(a>=kMc&&a<=lMc){c=(EKc(a>=0&&a<=lMc),a>=kMc?CE(xE(xF,1),eLc,46,15,[55296+(a-kMc>>10&mMc)&dLc,56320+(a-kMc&mMc)&dLc]):CE(xE(xF,1),eLc,46,15,[a&dLc]));vD(c[0],b);vD(c[1],b);return}htc(htc(htc(htc((b.a+="\\u",b),tD[a>>>12&15]),tD[a>>>8&15]),tD[a>>>4&15]),tD[a&15])}function yqb(a){var b,c;c=a.g;b=!a.g?null:a.g.g;switch(c.n.f){case 93:return true;case 91:return b.n==(Afc(), +Fec);case 94:return a.n!=(Afc(),cfc);case 116:if(a==c.c.f)return yqb(c);return false;case 114:return yqb(c);case 121:if(a==c.c)return yqb(c);return false;default:return false}}function zrb(a,b,c){var d,e,f,g,h;epb(a.b,c,new Yrb(a));fpb(a.b,new Qrb(a.b,a.a),b,c);f=new asb;g=new Bjb(a.b,new Erb(a));h=new dpb(a.b,f,g);cpb(h,b,c);e=new gob;fpb(a.b,e,b,c);Urb(new Vrb(a.b,a.a),b,c);d=new Grb;epb(a.b,c,d);d.a.a.Yd()==0||epb(a.b,c,new $rb(a,d.a));SZ(a.b.T)||(a.b.T=1)}function t2b(a){var b,c,d;if(!pF(a.a, +221))return null;c=a.a;b=Erc(c);d=null;if(isNaN(b))d="NaN";else if(b==Infinity)a.b.d?d="+Infinity":a.b.j?d=" Infinity":d=xQc;else if(b==-Infinity)a.b.g?d="(Infinity)":d="-Infinity";else return null;a.b.n=-1;a.b.k=false;return j2b(a,d,0)}function w4(a,b,c,d,e){var f,g;f=e==(_4(),Z4);f&&L3(a.c,"(");r4(a,b.n==(Afc(),Jdc)&&E8b(b,(Hbc(),Gac))!=0?_Nc:ZKc);if(E8b(b,(Hbc(),Xac))!=0){L3(a.c,"*");c.Wh().length==0||a.c.$g()}a.fh(c,W4);g=H8b(c,(n8b(),Y7b));!!g&&a.fh(g,W4);a.fh(c.f,W4);G4(a,b);a.fh(d,W4);a.c.Tg(e== +$4);f&&L3(a.c,")")}function tpb(a,b,c){ppb();var d;switch(a.n.f){case 103:case 28:case 23:return true;case 29:return!fqb(a,c)&&!b.Sd(a.Wh());case 26:case 27:return true;case 65:Yd(!(a.n==(Afc(),Jdc)&&iqb(a.g)&&a.n==Jdc&&Gqb(a.c)),a);return false}for(d=a.c;d;d=d.f)if(tpb(d,b,c))return true;return false}function YOb(a,b){if(!JOb(a,b,(NPb(),CPb)))return false;if((Pd(b.n==(Afc(),Ycc),iSc,b),B8b(b,1)).n!=bfc){a.a.Bh(URc+occ(jSc,CE(xE(iW,1),mLc,1,5,["property name"])),a.b.ug(),a.d,a.c);mPb(a,CPb.d);return false}if(!gPb(a, +(Pd(b.n==Ycc,iSc,b),B8b(b,2)))){mPb(a,CPb.d);return false}return true}function pp(a){bn();var b,c,d,e,f,g;g=new YEc;Dyc(g,a);for(c=(f=(new Wuc(g.a)).a.He().Vd(),new avc(f));c.a.Id();){b=(d=c.a.Jd(),d.Ve());Sd(b)}switch(g.a.Yd()){case 0:return mz(),lz;case 1:return new Zz((e=(new Wuc(g.a)).a.He().Vd(),d=(new avc(e)).a.Jd(),d.Ve()));default:return new nz(g)}}function w9(a){var b,c,d,e,f;d=(oo(),new hs);for(c=new yuc((new puc(a)).a);c.b;){b=wuc(c);e=b.Ve();f=b.We();if(qF(f))es(d,e,(ppb(),f?new J9b((Afc(), +nfc)):new J9b((Afc(),Edc))));else if(pF(f,132))es(d,e,O3b(f.a));else if(rF(f))es(d,e,O3b(f));else{Xd(tF(f));es(d,e,V3b(f))}}return Co(d.b)}function hqb(a,b){ppb();switch(a.n.f){case 71:return a.c==b;case 44:return a.c==b||(a.c?a.c.i:null)==b;case 72:case 73:case 101:case 102:case 70:case 83:case 77:case 78:return(a.c?a.c.i:null)==b;case 66:case 67:case 68:return a.c!=b;case 69:return true;default:Yd(gqb(a),a);return false}}function wGb(a){v6();this.a=a;new fDc;this.s=new tEc;this.u=new RHc;this.M= +new RHc;this.t=new RHc;new RHc;this.e=new z3;this.q=new mDc;this.F=(kwb(),new qwb(this));oo();this.c=new ayb;this.f=new ayb;this.R=new X7;this.L=new oub;this.d=new fxc;new tEc;new YEc;w6(this,this.L);this.D=new fDc}function Cd(a){var b,c,d,e,f,g,h;f=a.d;d="";c=itc(ptc(new ttc,a.a),123);for(h=a.b.b;h;h=h.b){g=h.c;if(!f||g!=null){c.a+=d;d=iLc;h.a!=null&&itc(ptc(c,h.a),61);if(g!=null&&(Bb(g).i&4)!=0){e=CE(xE(iW,1),mLc,1,5,[g]);b=Gxc(e,new mDc);ntc(c,b,1,b.length-1)}else c.a+=""+g}}return(c.a+="}",c).a} +function Ltb(a,b){Itb();var c;if(a9b(b,IQc,IQc.length)||b.n==(Afc(),Odc)&&_8b(b.c,mNc)&&b.c.f.n==bfc&&Esc(b.c.f.Wh(),"exports")){c=i$(Kob(a),mNc);if(!c||!c.a||B8(c.a))return true}else if(b.n==(Afc(),rec)&&Esc("exports",b.Wh())){c=i$(Kob(a),b.Wh());if(!c||!c.d.b)return true}return false}function dFb(a,b){ZEb();var c,d,e,f;e=a;!Esc(e.substr(0,"/".length),"/")&&!(Esc(e.substr(0,rOc.length),rOc)||Esc(e.substr(0,nRc.length),nRc))&&(e="/"+e);for(d=b.Vd();d.Id();){c=d.Jd();if(Esc(e.substr(0,c.length),c)){f= +Usc(e,c.length);if(Esc(f.substr(0,1),"/"))return f.substr(1)}}return a}function CUb(a){var b,c,d,e,f,g;e=DWb(a.g,0).d.b;iTb(a,(AZb(),oYb));d=jTb(a);c=tTb(a);f=(Bn(),new Zr);if(AVb(a,cYb)){iTb(a,cYb);g=nVb(a);Ywc(f.a,Sd(g));while(AVb(a,OXb)){iTb(a,OXb);g=nVb(a);!!g&&(Ywc(f.a,Sd(g)),f)}}iTb(a,FYb);b=EUb(a);iTb(a,KXb);return new h_b(new Z1b(e,a.f),d,c,In(f.a),b)}function D4(a){var b,c,d;d=new stc;for(c=0;c31&&b<127?(d.a+=String.fromCharCode(b),d):wD(d,b)}}return d.a}function Akb(a,b){var c,d,e,f;d=(b.c?b.c.i:null).Wh();e=Esc(d.substr(0,5),"goog:");if(e)return d.substr(5);else{f=oFb(z8((!a.d&&!!a.i&&(a.d=L6(a.c,a.i)),a.d)),d,(c=H8b(b,(Hbc(),ybc)),!c?null:c.ug()),T9b(b.k),S9b(b.k));!f&&(f=pFb(z8((!a.d&&!!a.i&&(a.d=L6(a.c,a.i)),a.d)),d));return UFb(f.a)}}function LRb(){IRb();return CE(xE(FR,1),sLc,24,0,[VQb,aRb,eRb,yRb,ARb,DRb,FQb,GQb,JQb,KQb,MQb,NQb,OQb,PQb,RQb, +SQb,EQb,lRb,XQb,ZQb,$Qb,fRb,gRb,kRb,oRb,sRb,tRb,xRb,zRb,BRb,CRb,iRb,WQb,HQb,ERb,mRb,nRb,IQb,YQb,rRb,uRb,vRb,wRb,HRb,pRb,TQb,CQb,DQb,LQb,QQb,UQb,_Qb,bRb,cRb,dRb,hRb,jRb,qRb,FRb,GRb])}function Ijc(a,b,c){var d,e,f,g,h;f=!a.g?null:a.g.e;g=!!f;a.g=new Xmc(aNc,b,true,!c?a.i:c);b.aj(a);!!f&&f.aj(null);if(a.oi()||a.f==(dkc(),bkc)){h=Bjc(a);if(h){!h.j&&(h.j=new fxc);Ywc(h.j,a)}if(a.f==(dkc(),bkc))for(e=a.c.Vd();e.Id();){d=e.Jd();!!d.Qi()&&ljc(d.Qi(),a)}}g&&pjc(a);return true}function yVb(a,b){var c,d,e,f; +f=DWb(a.g,0).d.b;iTb(a,(AZb(),zZb));d=false;c=null;e=DWb(a.g,0).d.b.b>a.f.b;if(!(e||zVb(a,0,ZYb)||zVb(a,0,KXb)||zVb(a,0,XXb))){d=!!mTb(a,bZb);FVb(a)?c=KTb(a,b):d&&VVb(a,"yield* requires an expression",CE(xE(iW,1),mLc,1,5,[]))}return new R1b(new Z1b(f,a.f),d,c)}function m6(){m6=JZ;new Mcb("JSC_UNSAFE_NAMESPACE",(E2(),D2),new Rtc("incomplete alias created for namespace {0}"));l6=new Mcb("JSC_NAMESPACE_REDEFINED",D2,new Rtc("namespace {0} should not be redefined"));new Mcb("JSC_UNSAFE_THIS",D2,new Rtc("dangerous use of ''this'' in static method {0}"))} +function Oib(a){var b,c,d;if(a.b.c!=0){b=P3b(CE(xE(HU,1),IMc,7,0,[]));for(d=(new VGc(a.b)).b.vj();d.Id();){c=d.Jd();Nib(a,b,c.Ve(),c.We())}r8b(a.g,I9b(t3b(l3b(Wqb(a.a,"Object.defineProperties"),CE(xE(HU,1),IMc,7,0,[(ae(Hsc(SPc,_sc(46))==-1,HMc,SPc),n8b(),new Wbc((Afc(),rec),SPc)),b]))),a.g))}}function uic(a){var b,c,d,e,f;d=(a.e&4)!=0;e=(a.e&2)!=0;f=(a.e&8)!=0;c=!a.a.b.b.Ud();c&&(a.n=a.f.u[51]);b=new Ljc(a.f,a.c,a.j,new hhc(a.f,a.d,a.g,d),a.n,a.k,a.b,e);!!a.i&&Hjc(b,a.i,null);f&&(b.b.b=(ae(b.f==(dkc(), +akc)||b.f==bkc,lRc,b),cgc(b.k)));c&&Fjc(b,new hkc(a.f,b,e,Ync(a.k,a.a)));return b}function G7(a){var b,c,d,e;e=(a.G.pd!=(Xab(),Uab)&&!!a.O&&wsb(a.O,lOc,true),new hyb);try{for(c=(!a.I&&(a.I=new CGb(a.a)),new wxc(BGb(a.I)));c.aa.f.b,!(c||zVb(a,0,(AZb(),ZYb))||zVb(a,0,(AZb(),KXb))||zVb(a,0,(AZb(),XXb))))){e=wTb(a);return new L1b(new Z1b(f,a.f),e,1,b)}else return b}}function $db(a,b){var c,d;d=b.g;c=b.c.n==(Afc(),tdc);ppb();if(b.n==bdc&&iqb(b.g)&&b.n==bdc&&Gqb(b.c)||c&&d.n==rec||c&&d.n==Ccc&&V8b(d.c)&&d.g.n==Ddc)return false;if(dkb(a.c,b)==0){u7(a.b,Lmb(b,(Rjb(),Pjb),CE(xE(nW,1),uNc,2,6,["class expression that cannot be extracted"])));return false}return true}function Krb(a, +b){var c,d,e;Xd(b.g.n==(Afc(),Jdc));d=null;c=b.c;while(!!c&&(ppb(),c.n==Jdc&&iqb(c.g)&&c.n==Jdc&&Gqb(c.c))){d=c;c=c.f}while(c){e=c.f;ppb();if(c.n==Jdc&&iqb(c.g)&&c.n==Jdc&&Gqb(c.c)){e9b(b,c);d=(!d?r8b(b,c):o8b(b,c,d),c);Nrb(a,"Move function declaration not at top of function",b)}c=e}}function $fb(a,b){var c,d,e,f,g,h,i;for(i=(f=(new fvc(a.j)).a.He().Vd(),new kvc(f));i.a.Id();){g=(d=i.a.Jd(),d.We());c=_0(g,x8b(a.d,false));I9b(c,a.c);o8b(b.g,c,b);b=c}for(h=(e=(new fvc(a.b)).a.He().Vd(),new kvc(e));h.a.Id();){g= +(d=h.a.Jd(),d.We());c=_0(g,x8b(a.g,false));I9b(c,a.c);o8b(b.g,c,b);b=c}}function Zob(a,b,c){var d;d=b.n;if(d==(Afc(),Xec)){Pob(a,b,c);return}else if(d==Jdc){Oob(a,b,c);return}a.e=b;if(!a.a.Bg(a,b,c))return;d==bdc?_ob(a,b):d==cdc?apb(a,b):d==pec?(Sob(a,b),$ob(a,b),Rob(a,false),undefined):a.q&&ypb(b)?(Sob(a,b),$ob(a,b),Rob(a,false),undefined):$ob(a,b);a.e=b;a.a.Cg(a,b,c)}function VOb(a,b){var c,d,e;if(!JOb(a,b,(NPb(),JPb)))return false;e=(Pd(b.n==(Afc(),Ycc),iSc,b),B8b(b,1));if(e.n!=bfc){a.a.Bh(URc+ +occ(kSc,CE(xE(iW,1),mLc,1,5,["native type"])),a.b.ug(),a.d,a.c);mPb(a,JPb.d);return false}d=(xMb(),c=CNb(e.Wh()),lNb(c,!c.o?mOb(c.j):GMb(c)));z8b(e);q8b(b,d);return true}function hVb(a){var b;if(zVb(a,0,(AZb(),iYb))&&Esc(DWb(a.g,0).a,GNc)&&(b=DWb(a.g,1).d.b.b>DWb(a.g,0).d.a.b,!(b||zVb(a,1,ZYb)||zVb(a,1,KXb)||zVb(a,1,XXb)))&&zVb(a,1,gYb))return NTb(a);if(zVb(a,0,gYb))return rUb(a);if(zVb(a,0,IXb))return WTb(a,false,false);if(AVb(a,sYb))return xVb(a);return lVb(a)}function C4(a,b){var c,d,e,f;e=b.Wh(); +f=b.Yh()||a.j&&Efc(e)||(G7b(),!F7b.test(e))||!wqb(e);if(f){d=Q4(e);isNaN(d)?y4(a,b):a.c.Kg(d,b)}else{if(E8b(b,(Hbc(),kbc))!=0){c=b.c;if(a9b(c,e,e.length)||c.n==(Afc(),mdc)&&_8b(c.c,e)){a.fh(c,(_4(),W4));return}}L3(a.c,e)}if(b.c){L3(a.c,":");v4(a,b.c,1,(_4(),W4))}}function Hqb(a){switch(a.f){case 13:case 3:case 20:case 1:case 2:case 46:case 16:case 4:case 18:case 9:case 27:case 26:case 8:case 40:case 41:case 7:case 10:case 6:case 17:case 15:case 5:case 19:case 11:case 36:case 37:case 14:case 25:case 79:case 21:case 22:case 12:return true; +default:return false}}function igc(a){var b,c,d,e,f,g,h;if(a.si())return a.A.u[60];else if(a.Mi()){h=a.Mi();g=false;c=(Yoc(new opc,h.b)&&Voc(h),h.a);for(f=0;f127)continue;if(uOb(b)){a.e=b;b=10}}a.a==-1&&(a.a=a.i-a.f-a.r-1);return b}}function Lyb(a,b,c,d){Fyb();var e,f,g,h,i,j,k,l,m;if(m9(a.G,c)){j=l9(a.G);for(l=(!b.c?(Cyc(),Cyc(),Byc):new Kbc(b.c)).Vd();l.Id();){k=l.Jd();i=(ppb(),Yd(k.n==(Afc(),Xec),k),H8b(k,(n8b(),V7b)));if(!!i&&!j.a.Td(i.a))for(f=d,g=0,h=f.length;gf.length)return null; +return f.substr(d,c-d)}function q7(b,c){var d,e,f,g;f=new nvb(b);for(e=new Tu(wu($t(c.a),new Xt));Ru(e);){d=Su(e);if(!Dsc(d.a.sh().b,".json"))continue;d.b=b;try{Swb(d.a.sh(),"("+d.a.sh().a+")")}catch(a){a=fZ(a);if(pF(a,104))continue;else throw gZ(a);}g=Sd(x8(d,b));D8(d,(b9(),_8));Xd(g.n==(Afc(),Xec));epb(f.a,g,f)}return wo(f.b)}function udb(a,b){var c,d,e,f,g,h,i;Ld(V8b(a),a);Ld(b.n==(Afc(),Ycc),b);d=g9b(b);Yd(d.n==Pdc,d);c=Sd(d.c.f);Yd(Esc(c.Wh(),hPc),c);i=d.c;j9b(d,i,G9b(x8b(a,false),i));f=b.c; +Yd(f.n==Aec,f);e9b(b,f);e=F9b((h=Tqb(d,CE(xE(HU,1),IMc,7,0,[])),h),b);q8b(e,F9b((g=new J9b(lfc),g),f));while(b.c)q8b(e,g9b(b));return e}function Yib(a,b,c,d){var e,f,g,h,i;e=c.c;g=null;(e.n==(Afc(),Jdc)||e.n==bdc)&&(g=(ppb(),h=Ppb(e),!h?null:I8b(h)));if(g!=null){f=z8b(e);j9b(d,c,f)}else{g="$$default";i=q3b((ae(Hsc(g,_sc(46))==-1,HMc,g),n8b(),new Wbc(rec,g)),g9b(c),gdc);j9b(d,c,I9b(i,c));qpb(b.f,(IRb(),RQb))}qGc(a.b,JNc,new Mib(g,c));Vob(b)}function _Mb(a,b){var c,d,e,f,g;d=QMb(a,(Afc(),gec));f=new mDc; +do{c=$Mb(a,b);if(!c)return null;e=c.n==cfc?c.Wh():c.c.Wh();g=f.a.Je(e,f);g==null?q8b(d,c):DMb(a,e,a.j.g,lOb(a.j));xNb(a);if(!OMb(a,(fOb(),PNb)))break;!a.o?mOb(a.j):GMb(a);xNb(a);if(OMb(a,aOb))break;b=!a.o?mOb(a.j):GMb(a)}while(true);return d}function yjc(a,b){var c,d,e;if((cnc(a.t,b)?1:0)==0){e=Esc(ePc,b);d=Esc("bind",b);if(e||d)zhc(a,b,rjc(a,e),a.i);else if(Esc(hPc,b)){c=new Qic(a.A);Mic(c,CE(xE(cV,1),xPc,23,0,[Rkc(a.A,Yfc(a.k)?a.A.u[51]:a.k),Rkc(a.A,a.A.u[38])]));zhc(a,b,uic(Hic(Dic(Bic(new Lic(a.A), +c.b),a.b.b),a.B.c)),a.i)}}return Chc(a,b)}function rf(a,b){var c,d,e,f,g;a=a;for(e=0;e=0,"Invalid module index: %s",c);b.c=c;this.a[c]=b}this.b=enb(this);this.c=gnb(this);fnb(this);knb(cnb(this),Tt(Wt(new eyc(this.a),new lnb)))}function B_(a,b,c){var d;F_(a,(IRb(),iRb),b);b0((Afc(),Adc),b);if(E8b(b,(n8b(),T7b))!=0){d=C8b(b);2!=d&&D0(PMc+d,b);b0(tdc,b.c);s0(b.c.f)}else if(E8b(b,U7b)!=0){d=C8b(b);1!=d&&D0(UMc+d,b);E_(a,b.c)}else{d=C8b(b);(d<1||d>2)&&D0(VMc+d,b);b.c.n==Cdc?D_(b.c):q0(a,b.c,c);!!b.c&&!!b.c.f&&b.c.f==(b.c?b.c.i: +null)&&s0(b.c.f)}}function $Ob(a,b){var c,d;if(b.n==(Afc(),Eec))for(d=(!b.c?(Cyc(),Cyc(),Byc):new Kbc(b.c)).Vd();d.Id();){c=d.Jd();if(E8b(c,(Hbc(),kbc))!=0){a.a.Bh(URc+occ(jSc,CE(xE(iW,1),mLc,1,5,["property, missing type"])),a.b.ug(),a.d,a.c);return false}else if(!gPb(a,c.c))return false}else if(!gPb(a,b))return false;return true}function JC(a,b){var c,d,e,f,g,h,i,j;f=false;i=duc(a.k.c);c=new gxc;Zwc(c,Gyc(i,""));for(e=new TEc(new KEc(a.j));e.b!=e.c.a.b;){d=REc(e);h=pEc(a.k,d.d);if(!!h&&h.a>19!=0)return"-"+cF(XE(a));c=a;d="";while(!(c.l==0&&c.m==0&&c.h==0)){e=FE(1E9);c=IE(c,e,true);b=""+bF(EE); +if(!(c.l==0&&c.m==0&&c.h==0)){f=9-b.length;for(;f>0;f--)b="0"+b}d=b+d}return d}function z6(a,b,c,d){var e,f;Xd(!a.v);try{Y6(a,b,c,d);a.g.a.d!=0||r8(new h8(a));if(a.g.a.d==0){Yd(!!a.B,dOc);Xd(a.g.a.d==0);Xd(true);r8(new b8(a));a.g.a.d!=0||B7(a);r8(new f8(a))}}finally{e=(a.G.pd!=(Xab(),Uab)&&!!a.O&&wsb(a.O,eOc,true),new hyb);y1(a.g.a);f=vZ(mZ(Date.now()),e.a);a.G.pd!=Uab&&!!a.O&&xsb(a.O,eOc,f)}return O6(a)}function eFb(a,b,c){var d,e,f,g,h;h=(bn(),new Rs);e=new mDc;for(g=wu(a.a.Vd(),a.b);g.b.Id();){f= +Ju(g,g.b.Jd());d=c.Ph(f);if(!jDc(e,dFb(d,b)))throw gZ(new qnb("Duplicate module path after resolving: "+f));!Esc(d.substr(0,1),"/")&&!(Esc(d.substr(0,2),rOc)||Esc(d.substr(0,3),nRc))&&(d="/"+d);Ywc(h.b,Sd(d))}return np(new wxc(h.b))}function _Fb(a,b,c){var d,e,f,g,h;h=(ZEb(),(!Esc(b.substr(0,"/".length),"/")&&!(Esc(b.substr(0,rOc.length),rOc)||Esc(b.substr(0,nRc.length),nRc))?"/":"")+b);for(g=xu(new Wzc(a.b.b.b.Vd()));g.Id();){f=g.Jd();if(!Esc(h.substr(0,f.length),f))continue;d=f+qRc+c;e=$Fb(a,b, +d);e==null&&(e=aGb(a,b,d));if(e!=null)return e}return null}function nHb(){nHb=JZ;mHb=new Mcb("JSC_MUTATED_EXPORT",(E2(),D2),new Rtc('The name "{0}" is exported and should not be mutated outside of module initialization. Mutable exports are generally difficult to reason about. You can work around this by exporting getter/setter functions, or an object with mutable properties instead.'))}function KLb(a,b){var c,d;switch(b.a.e.f){case 100:return c=lKb(a.a,b.a),d=hKb(a.a,c),T9b(d.k)==-1&&vKb(d,b.o.b, +b.o.a),d;case 101:return _Lb(a,b);case 99:case 98:return dKb(a.a,SKb(b.a.e));case 97:return dKb(a.a,(Afc(),Aec));case 102:return YLb(a,b);default:throw gZ(new Irc("Unexpected literal type: "+b.a.Fj+" type: "+b.a.e));}}function vVb(a,b,c){var d,e,f,g;f=DWb(a.g,0).d.b;g=null;if(zVb(a,0,(AZb(),HYb))||zVb(a,0,FYb))e=UUb(a,0);else{e=wUb(a);AVb(a,NXb)&&(g=(iTb(a,NXb),nVb(a)))}d=null;AVb(a,ZXb)?d=(iTb(a,ZXb),KTb(a,c)):c!=0&&(b==PXb?VVb(a,TSc,CE(xE(iW,1),mLc,1,5,[])):GZb(e)&&VVb(a,USc,CE(xE(iW,1),mLc,1,5, +[])));return new N1b(new Z1b(f,a.f),e,g,d)}function lC(a){var b,c,d,e;e=zE(yF,iMc,46,5,15,1);b=new fxc;while(EC(a.a))if(pC(a,59)){mC(a,b);b.a.length==0||(b=new fxc)}else{d=0;while(!oC(a)){e[d]=YB(a.a);++d}c=nC(a,e,d);$d(a.i.b<0||a.bn&& +ntc(a.c,b,n,m);h=m;if(h=0){++i;h=Isc(f,_sc(46),h+1)}}if(!!e&&Sqb(d))return true;for(g=0;g0? +(e.i=(JNb(),HNb),xNb(e),h=!e.o?mOb(e.j):GMb(e),i=JMb(e,h,0,false,true),j=" "+i.a,e.d?i6b(e.d,j):e.f.d?t6b(e.f,j):t6b(e.f,""),undefined):TMb(e);return e}function tqb(a){ppb();var b,c;switch(a.n.f){case 31:case 30:case 32:case 35:case 34:return true;case 92:case 19:case 79:case 22:return tqb(a.c);case 29:c=a.Wh();return Esc(vPc,c)||Esc(xQc,c)||Esc("NaN",c);case 118:for(b=a.c;b;b=b.f)if(b.n==(Afc(),kfc))if(!tqb(b.c))return false;return true;default:Kd(a.n!=(Afc(),jfc))}return false}function egb(a,b){var c, +d,e,f,g,h,i,j;i=agb(a,b);g=z8b(a.c?a.c.i:null);c=H9b((j=h3b(i,g),j),g);e=H8b(a,(Hbc(),mbc));if(E8b(a,zbc)!=0&&brb(c.c?c.c.i:null)){f=E7b(e);p7b(f,new H7b(new L9b((Afc(),Qcc),new J9b(Pec)),(d=H8b(a,ybc),!d?null:d.ug())));e=k6b(f)}!!e&&(d9b(c,mbc,e),c);h=(ppb(),F9b((Yd(H3b(c),c),new L9b((Afc(),Ddc),c)),c));sgb(b.i,h)}function GTb(a){var b,c,d;c=DWb(a.g,0).d.b;b=(Bn(),new Zr);iTb(a,(AZb(),HYb));d=null;while(AVb(a,OXb)||AVb(a,aZb)||FVb(a)){d=null;if(AVb(a,OXb))Xr(b,new s_b(pTb(a,DWb(a.g,0).d.b)));else if(AVb(a, +aZb)){RVb(a,(IRb(),zRb));Xr(b,jVb(a))}else Xr(b,KTb(a,1));AVb(a,MXb)||(d=iTb(a,OXb))}iTb(a,MXb);vTb(a,d);return new MZb(new Z1b(c,a.f),In(b.a))}function N6(a,b){switch(b.f){case 0:return zJb(),pJb;case 1:case 2:return zJb(),qJb;case 3:return zJb(),rJb;case 4:return zJb(),xJb;case 5:return zJb(),sJb;case 6:return zJb(),tJb;case 7:return zJb(),uJb;case 8:return zJb(),vJb;case 12:return zJb(),yJb;case 9:return zJb(),wJb;default:throw gZ(new Irc("Unexpected language mode: "+a.G.Sb));}}function tVb(a){var b, +c,d,e,f;d=DWb(a.g,0).d.b;if(OVb(a)){c=wTb(a);b=tVb(a);return new J1b(new Z1b(d,a.f),c,b)}else return zVb(a,0,(AZb(),iYb))&&Esc(DWb(a.g,0).a,ZSc)?(e=DWb(a.g,0).d.b,(swc(a.e)||!uwc(a.e).a)&&VVb(a,"'await' used in a non-async function context",CE(xE(iW,1),mLc,1,5,[])),oTb(a,ZSc),f=tVb(a),new QZb(new Z1b(e,a.f),f)):uVb(a)}function Ljc(a,b,c,d,e,f,g,h){Ofc();Wic.call(this,a,b,a.u[17],h,f);this.d=(Bn(),Bn(),An);this.c=(null,An);this.j=null;this.s=true;Kd(!c||c.n==(Afc(),Jdc)||c.n==(Afc(),bdc));Sd(d);this.i= +c;this.f=g;switch(g.f){case 1:this.k=e?e:new gkc(a,this,h);break;case 0:this.k=e?e:a.u[51];break;case 2:this.k=e?e:new gkc(a,this,h)}this.b=d;this.e=false}function L2b(a,b){var c;c=false;switch(b){case 43:c=a.d;a.d=true;break;case 44:c=a.e;a.e=true;break;case 45:c=a.f;a.f=true;break;case 40:c=a.g;a.g=true;break;case 35:c=a.i;a.i=true;break;case 32:c=a.j;a.j=true;break;case 48:c=a.k;a.k=true;break;default:return false}if(c)throw gZ(new GD(""+b));!a.o&&(a.o=new ttc);itc(a.o,b&dLc);return true}function Lv(a, +b,c,d){var e,f,g;g=new vw(b,c);if(!a.a){a.a=a.e=g;$tc(a.b,b,new uw(g));++a.c}else if(!d){a.e.b=g;g.d=a.e;a.e=g;e=Xtc(a.b,b);if(!e){$tc(a.b,b,e=new uw(g));++a.c}else{++e.a;f=e.c;f.c=g;g.e=f;e.c=g}}else{e=Xtc(a.b,b);++e.a;g.d=d.d;g.e=d.e;g.b=d;g.c=d;!d.e?Xtc(a.b,b).b=g:d.e.c=g;!d.d?a.a=g:d.d.b=g;d.d=g;d.e=g}++a.d;return g}function xB(a,b,c){var d,e,f;Sd(b);c=pB(a,c);if(!iB(a.b,btc(c)))throw gZ(new QB("Invalid input length "+btc(c)));d=0;for(f=0;f>>16<<24>>24;if(f>>8&255)<<24>>24;if(f>24}}}return d}function BWb(a,b,c){var d,e,f;d=a.f;e=cXb(a);!xWb(a,a.f)&&EWb(a,ZSb(a.i.b,d),$Sc,CE(xE(iW,1),mLc,1,5,[]));f=Vsc(a.b,d,a.f);switch(xWb(a,a.f)?zsc(a.b,a.f):0){case 96:yWb(a);return new uXb(b,f,e.a,e.b,$Sb(a.i.b,d-1,a.f));case 36:yWb(a);yWb(a);return new uXb(c,f,e.a,e.b,$Sb(a.i.b,d-1,a.f));default:return new uXb(b,f,e.a,e.b,$Sb(a.i.b,d-1,a.f))}} +function llc(){this.B=new fq(new tEc,new iq);this.t=new J9b((Afc(),Vec));this.v=new YEc;this.D=jy(Zx((fl(8,lQc),new $x)));this.A=new mDc;this.e=new mDc;this.f=new tEc;this.j=new fDc;this.q=new hv;new fxc;this.g=new $nc(this,(Bn(),Bn(),An),(null,An));this.u=zE(cV,xPc,23,Kkc().length,0,1);Sf(this.D);nEc(this.f);clc(this);nEc(this.B.a);dlc(this)}function Drb(a,b,c,d){var e,f,g;if(b.c){e9b(c,b);g=b.c;e9b(b,g);e=h3b(b,g);q9b(e,H8b(c,(Hbc(),mbc)));H9b(e,c);f=(ppb(),F9b((Yd(H3b(e),e),new L9b((Afc(),Ddc), +e)),e));j9b(d,c,f);Arb(a.b,BQc,f)}else{ppb();if(d.n==(Afc(),Vec)||d.n==Xec||d.n==Vcc||d.n==pec)e9b(d,c);else if(d.n==Hdc||d.n==Idc){e9b(c,b);j9b(d,c,b)}else Yd(d.n==eec,d);Arb(a.b,BQc,d)}}function Axb(b,c){var d,e,f,g,h,i;if(!b.a){b.a=true;i=b.c.b;try{g=b.c.a;d=new iC;h=qD(g);eC(d,h);b.b=d}catch(a){a=fZ(a);if(pF(a,104)){e=a;f=new Gmb(null,null,-1,-1,yxb,null,CE(xE(nW,1),uNc,2,6,[i,e.g]));Xxb(c,f.b,f)}else if(pF(a,127)){e=a;f=new Gmb(null,null,-1,-1,xxb,null,CE(xE(nW,1),uNc,2,6,[i,e.g]));Xxb(c,f.b, +f)}else throw gZ(a);}}return b.b}function UBb(a,b){var c,d,e,f,g;Yd(b.n==(Afc(),Jdc)&&b.g.n==mec,b);g=b.d;if(!g||g.Bi())return"";c=g.Ii();f=new vtc("/**\n");d=(ppb(),Kd(b.n==Jdc),b.c.f).c;OBb(a,f,c,d);if(mqb(b))QBb(f,sjc(c));else{QBb(f,Vfc(c));e=c.b.b;if(!!e&&!(e.ti()||e.ri()||e.si()||e==e.A.u[64])){f.a+=mRc;aCb(f,ENc,Pfc(e,new stc,true).a);f.a+=gMc}}f.a+=" */\n";return f.a}function onc(a,b,c){Ofc();var d,e,f,g;Uic.call(this,a,null,null);this.a=false;this.s=true;for(g=xu(new Wzc((d=b.b,!d?b.b=At(b.a, +sAc(b.c)):d).b.b.Vd()));g.Id();){f=g.Jd();e=f==null?null:Bx(b.d,f);if(!e)throw gZ(new Irc("RecordProperty associated with a property should not be null!"));c?zhc(this,f,e.b,e.a):lnc(this,f,e.b,false,e.a)}this.a=true}function IC(a,b,c,d,e,f){var g,h,i,j,k,l,m,n,o,p;if(b==null||d.b<0)return;h=e;g=f;if(a.f.b!=0||a.f.a!=0){o=a.f.b;p=a.f.a;i=a.f.a;e.b>0&&(p=0);f.b>0&&(i=0);h=new _B(e.b+o,e.a+p);g=new _B(f.b+o,f.a+i)}l=new YC;l.d=b;l.c=d;l.b=c;l.e=h;l.a=g;if(a.b){k=a.b.e.b;j=a.b.e.a;n=l.e.b;m=l.e.a;de(n> +k||n==k&&m>=j,Yrc(k),Yrc(j),Yrc(n),Yrc(m))}a.b=l;Ywc(a.e,l)}function SFb(a){RFb();var b,c,d,e,f,g,h,i,j;i=Rsc(a,"/",0);d=zE(nW,uNc,2,i.length,6,1);j=0;c=0;b=i.length>1&&i[0].length==0;b&&--c;for(f=i,g=0,h=f.length;g0){--j;--c;d[j]=null}else if(!b){d[j]=e;++j}continue}d[j]=e;++j;++c}if(b&&j==1)return"/";return Nc(QFb,new Guc(new eyc(Fxc(d,j))))}function pD(a){var b,c;c=a.offset.line;b=a.offset.column;if(a.map!=null&&a.url!=null)throw gZ(new rD("Invalid map format: section may not have both 'map' and 'url'")); +else if(a.url!=null)return new sD(0,a.url,c,b);else if(a.map!=null)return new sD(1,etc(a.map),c,b);throw gZ(new rD("Invalid map format: section must have either 'map' or 'url'"));}function tvb(a,b,c){var d,e,f,g,h,i,j;Kd(c.n==(Afc(),Eec));g=Z$((Kob(b),CE(xE(HU,1),IMc,7,0,[(h=P3b(CE(xE(HU,1),IMc,7,0,[])),h)])));j=null;for(e=(!c.c?(Cyc(),Cyc(),Byc):new Kbc(c.c)).Vd();e.Id();){d=e.Jd();if(d.n==_ec){i=g9b(d);q8b(g,i);j=null}else{if(!j){j=(f=P3b(CE(xE(HU,1),IMc,7,0,[])),f);q8b(g,j)}q8b(j,z8b(d))}}I9b(g, +c);j9b(c.g,c,g);w7(a.a,g)}function BQb(a){iQb();switch(a){case "es3":return YPb;case "es5":return ZPb;case "es6-impl":case kOc:return $Pb;case "typeCheckSupported":return hQb;case "es7":return aQb;case "es8":return cQb;case "es2018":case "es9":return UPb;case "es_2019":return WPb;case rRc:return eQb;case "ts":return gQb;default:throw gZ(new qnb("No such FeatureSet: "+a));}}function tq(a,b,c){var d,e,f,g,h;d=yZ(rZ(VLc,Vrc(yZ(rZ(b==null?0:Db(b),WLc)),15)));h=yZ(rZ(VLc,Vrc(yZ(rZ(c==null?0:Db(c),WLc)), +15)));f=wq(a,b,d);if(!!f&&h==f.f&&Gd(c,f.i))return c;g=xq(a,c,h);if(g)throw gZ(new qnb("value already present: "+c));e=new $q(b,d,c,h);if(f){pq(a,f);rq(a,e,f);f.e=null;f.c=null;return f.i}else{rq(a,e,null);vq(a);return null}}function A3(a){var b;this.b=a;this.a=tp("goog.isDef","goog.isNull","goog.isDefAndNotNull","goog.isString","goog.isNumber","goog.isBoolean",CE(xE(nW,1),uNc,2,6,["goog.isFunction","goog.isArray","goog.isArrayLike","goog.isObject"]));b=(bn(),new Rs);Ps(b,CE(xE(nW,1),uNc,2,6,["superClass_", +"instance_","getInstance"]));Qs(b,(Bn(),Bn(),An));np(new wxc(b.b))}function Ogb(a){Bgb();this.e=new Cwc;this.a=a.a;this.f=a.b;switch(this.f.f){case 0:this.d=rQb((iQb(),TPb),CE(xE(FR,1),sLc,24,0,[(IRb(),SQb),EQb,FQb,lRb]));this.c=oQb(this.d,nRb);break;case 1:this.d=oQb((iQb(),TPb),(IRb(),nRb));this.c=this.d;break;default:throw gZ(new Kqc("Es6RewriteDestructuring cannot handle ObjectDestructuringRewriteMode "+this.f));}}function _Ib(a){var b;b="";!a.d&&(b+=" languageMode");!a.g&&(b+=" strictMode"); +!a.c&&(b+=" jsDocParsingMode");!a.f&&(b+=" runMode");!a.a&&(b+=" annotations");!a.i&&(b+=" suppressionNames");!a.b&&(b+=" closurePrimitiveNames");a.e==null&&(b+=" parseInlineSourceMaps");if(b.length!=0)throw gZ(new Irc(bNc+b));return new XIb(a.d,a.g,a.c,a.f,a.a,a.i,a.b,a.e)}function Epb(a){ppb();var b,c;c=Vpb(a);b=c.g;if(b.n==(Afc(),Sec)||b.n==mdc){b=b.g;Yd(b.n==Jec,b)}else if(b.n==odc){b=b.g;Yd(!!b&&(b.n==ufc||b.n==jec||b.n==gdc),b)}else if(b.n==bdc||b.n==Jdc)Yd(a==b.c,a);else if(b.n==Wdc){Yd(a== +b.c.f,a);b=!b.g?null:b.g.g;Yd(b.n==Vdc,b)}else Yd(b.n==Jec||!!b&&(b.n==ufc||b.n==jec||b.n==gdc)||b.n==Vdc||b.n==adc,b);return b}function Bib(a,b){if(b.g.n==(Afc(),Ddc))return;dkb(a.a,b)!=0?gkb(a.a,b):u7(a.b.b,Lmb(b,(Rjb(),Pjb),CE(xE(nW,1),uNc,2,6,["Undecomposable expression: Please rewrite the yield or await as a separate statement.\nSee https://github.com/google/closure-compiler/wiki/FAQ#i-get-an-undecomposable-expression-error-for-my-yield-or-await-expression-what-do-i-do"])))}function p0(a,b){var c, +d,e;b0((Afc(),_ec),b);d=Cfc(b.n);d!=-1&&(c=C8b(b),d!=c&&D0(QMc+d+RMc+c,b));e=b.g;switch(e.n.f){case 28:case 23:b==e.c&&D0("SPREAD node is not callable.",b);F_(a,(IRb(),zRb),b);break;case 43:F_(a,(IRb(),mRb),b);F_(a,zRb,b);break;case 42:F_(a,(IRb(),zRb),b);break;default:D0("SPREAD node should not be the child of a "+e.n+" node.",b)}}function _nc(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q;o=b.c;l=c.c;for(f=0;f4&&(zsc(a.b,c+2)==42?d=b$b:zsc(a.b,c+2)==33&&(d=a$b));b=$Sb(a.i.b,c,a.f);e=Vsc(a.b,c,a.f);$Vb(a.a,d,b,e)}else FWb(a,"unterminated comment", +CE(xE(iW,1),mLc,1,5,[]))}function YDb(a){var b;b="";a.d==null&&(b+=" name");a.e==null&&(b+=" pathRelativeToClosureBase");!a.f&&(b+=" provides");!a.g&&(b+=" requires");!a.i&&(b+=" typeRequires");!a.c&&(b+=" loadFlags");a.a==null&&(b+=" hasExternsAnnotation");a.b==null&&(b+=" hasNoCompileAnnotation");if(b.length!=0)throw gZ(new Irc(bNc+b));return new ODb(a.d,a.e,a.f,a.g,a.i,a.c,a.a,a.b)}function sNb(a,b,c){var d,e,f;f=QMb(a,(Afc(),Kec));!!c&&q8b(f,c);d=null;do{if(d){xNb(a);b=!a.o?mOb(a.j):GMb(a);Xd(b== +(fOb(),ZNb));xNb(a);b=!a.o?mOb(a.j):GMb(a)}d=mNb(a,b);if(!d)return null;q8b(f,d)}while(OMb(a,(fOb(),ZNb)));if(!c){xNb(a);if(!OMb(a,bOb))return CMb(a,aSc,a.j.g,lOb(a.j)),null;!a.o?mOb(a.j):GMb(a)}if(!!f.c&&!f.c.f){e=f.c;e9b(f,e);return e}return f}function Tnb(a,b,c){var d,e,f,g,h,i,j,k,l;j=a.e;l=b.f;g=b.d;f=b.a;d=new stc;e=new stc;i=Ynb(l,g);if(a.c){h=!j?null:S6(j,b.f,b.d,b.a);if(!h)e.a+=""+i;else{l=h.d;g=h.c;f=h.a;d.a+=""+i;d.a+="\nOriginally at:\n";ptc(e,Ynb(l,g))}}if(a.b){ptc(e,UZ(a,c?(E2(),D2): +(E2(),B2)));e.a+=" - "}ptc(e,b.c);ptc(d,WZ(a,e.a));d.a+=gMc;k=Unb(a,b,l,g,f);k!=null&&(d.a+=""+k,d);return d.a}function Unb(a,b,c,d,e){var f,g,h,i,j,k,l;f=new stc;k=a.e;l=!k?null:R6(k,c,d);if(l!=null){f.a+=""+l;f.a+=gMc;if(a.a==(Lwb(),Jwb)&&0<=e&&e<=l.length){for(i=0;i=0){if(a.e==13&&b==10){a.e=10;continue}a.e=-1;a.f=a.i-1;++a.g}if(b<=127){if(b==10||b==13){a.e=b;b=10}}else{Gfc(); +if(b>127)continue;if(uOb(b)){a.e=b;b=10}}a.a==-1&&(a.a=a.i-a.f-a.r-1);return b}}function nC(a,b,c){var d;switch(c){case 1:d=new rC(b[0]+a.c);a.c=d.e;return d;case 4:d=new xC(b[0]+a.c,b[1]+a.f,b[2]+a.g,b[3]+a.e);a.c=d.e;a.f=d.bg();a.g=d.cg();a.e=d.ag();return d;case 5:d=new CC(b[0]+a.c,b[1]+a.f,b[2]+a.g,b[3]+a.e,b[4]+a.d);a.c=d.e;a.f=d.bg();a.g=d.cg();a.e=d.ag();a.d=d._f();return d;default:throw gZ(new rD("Unexpected number of values for entry:"+c));}}function Scb(){Scb=JZ;Qcb=new Mcb("JSC_INVALIDATION", +(E2(),C2),new Rtc("Property disambiguator skipping all instances of property {0} because of type {1} node {2}. {3}"));Rcb=new Mcb("JSC_INVALIDATION_TYPE",C2,new Rtc("Property disambiguator skipping instances of property {0} on type {1}. {2}"));new Mcb("JSC_INVALID_RENAME_FUNCTION",B2,new Rtc("{0} call is invalid: {1}"))}function L3(a,b){var c,d,e;V3(a);if(b.length==0)return;c=b.charCodeAt(0);(c==95||c==36||(Qqc==null&&(Qqc=new RegExp(hLc,"i")),Qqc.test(String.fromCharCode(c)))||c==92)&&g4(a.Xg())? +a.Lg(" "):c==47&&a.Xg()==47?a.Lg(" "):(c==34||c==39)&&g4(a.Xg())&&a.$g();e=0;d=Hsc(b,_sc(10));while(d>=0){d>e&&a.Lg(b.substr(e,d-e));a.dh();e=d+1;d=Isc(b,_sc(10),e)}b.length>e&&a.Lg(b.substr(e))}function Gnb(a,b){var c,d,e,f,g,h,i;i=new fxc;if(a.Mi())for(d=Poc(a.Mi()).Vd();d.Id();){c=d.Jd();c=c.fi()?c.fi():c;g=c.zi(b.u[24]);f=c.zi(b.u[3]);if(!g&&!f)return new Inb(null);h=f?3:24;Ywc(i,Ufc(c,b.u[h]))}else{e=a.fi()?a.fi():a;g=e.zi(b.u[24]);f=e.zi(b.u[3]);if(!g&&!f)return new Inb(null);h=f?3:24;Ywc(i, +Ufc(e,b.u[h]))}return new Inb(Zkc(b,exc(i,zE(cV,xPc,23,0,0,1))))}function XBb(a,b){var c,d,e;if(b.n==(Afc(),mec))return UBb(a,(Xd(!!b.c&&!b.c.f),b.c));else if(b.n==bdc)return SBb(b.d);else if(b.n==Jdc)return TBb(a,b);else{d=(ppb(),c=Dpb(b),!!(!c?null:H8b(c,(Hbc(),mbc))));if(!d)return"";e=b.d;return!e?"":e.Ii()?TBb(a,b):(e.Hi(),!e.Bi()&&!(e.ti()||e.ri()||e.si()||e==e.A.u[64])&&!e.Ci()&&!e.qi()?"/** @type {"+agc(b.d,0)+"} */\n":"")}}function nlb(a,b){var c,d,e,f,g,h;h=b.g;Xd(h.n==(Afc(),Eec));c=h.g; +if(!c)return null;d=c.g;switch(c.n.f){case 29:if(!d||(ppb(),!(!!d&&(d.n==ufc||d.n==jec||d.n==gdc))))return null;g=c.Wh();break;case 47:f=c.c;g=I8b(f);break;case 91:if(!!d&&d.n==Eec)g=nlb(a,c);else return null;break;default:return null}if(g!=null){e=b.Wh();G7b();if(F7b.test(e))return g+"."+e}return null}function HTb(a,b){var c,d,e;e=DWb(a.g,0).d.b;c=(Bn(),new Zr);iTb(a,(AZb(),HYb));while(AVb(a,OXb)||FVb(a))if(AVb(a,OXb)){d=DWb(a.g,0).d.b;iTb(a,OXb);Xr(c,new s_b(new Z1b(d,a.f)))}else{Xr(c,VUb(a,b)); +if(AVb(a,OXb))iTb(a,OXb);else break}if(AVb(a,aZb)){RVb(a,(IRb(),FQb));Xr(c,XUb(a,b))}if(!iTb(a,MXb))return new n_b(pTb(a,DWb(a.g,0).d.b));return new NZb(new Z1b(e,a.f),In(c.a))}function a9b(a,b,c){var d,e,f;f=Ksc(b,_sc(46),c-1)+1;switch(a.n.f){case 29:d=a.Wh();return f==0&&d.length!=0&&d.length==c&&Esc(b.substr(0,d.length),d);case 33:return f==0&&4==c&&Esc(b.substr(0,4),FMc);case 99:return f==0&&5==c&&Esc(b.substr(0,5),DMc);case 26:e=(a.c?a.c.i:null).Wh();return f>1&&e.length==c-f&&Msc(e,false,0, +b,f,c-f)&&a9b(a.c,b,f-1);case 98:default:return false}}function wgb(a,b){var c,d,e,f;d=Td((ppb(),Gpb(b,new jrb)),b);if(d==b)return true;else{c=b.g;if(!!d&&(d.n==(Afc(),ufc)||d.n==jec||d.n==gdc)&&c.n==(Afc(),rec)&&d==c.g&&!(c==c.g.c?null:c.i))return true;else if(d.n==(Afc(),Ddc)&&d==c.g&&!(c==c.g.c?null:c.i)&&!c.f&&c.n==Ccc&&(f=b==b.g.c?null:b.i,!!f&&c==f.g&&!(f==f.g.c?null:f.i))){e=c.c;return!upb(e,false,a.b)}else return false}}function YBb(a,b){var c,d,e;d=b.g;if(!d)return;if(d.n==(Afc(),Vcc)||d.n== +Xec||d.n==cdc)if(b.n==bdc||b.n==Jdc||b.n==mec)r4(a,XBb(a,b));else if(b.n==Ddc&&b.c.n==Ccc){c=b.c;if(Dqb(c.c))r4(a,Bmb(a.a,H8b(c,(Hbc(),mbc))));else{e=c.c?c.c.i:null;r4(a,XBb(a,e))}}else{ppb();!!b&&(b.n==ufc||b.n==jec||b.n==gdc)&&!!b.c.c&&(Dqb(b.c)?r4(a,Bmb(a.a,H8b(b,(Hbc(),mbc)))):r4(a,XBb(a,b.c.c)))}}function teb(a,b,c){var d,e,f,g;f=c.c;e=f.f.Wh();if(seb(b,f)){C6(a.a,zPc,false);g=(ppb(),Gpb(c,new jrb));switch(e){case "iterator":{d=G9b(t3b(l3b(Wqb(a.a,"$jscomp.initSymbolIterator"),CE(xE(HU,1),IMc, +7,0,[]))),g);p8b(g.g,d,g);w7(a.a,d);break}case "asyncIterator":{d=G9b(t3b(l3b(Wqb(a.a,"$jscomp.initSymbolAsyncIterator"),CE(xE(HU,1),IMc,7,0,[]))),g);p8b(g.g,d,g);w7(a.a,d);break}}}}function Rpb(a){ppb();switch(a.n.f){case 48:return Afc(),Tcc;case 49:return Afc(),Ucc;case 50:return Afc(),Rcc;case 51:return Afc(),kec;case 52:return Afc(),Wec;case 53:return Afc(),tfc;case 54:return Afc(),vcc;case 55:return Afc(),efc;case 56:return Afc(),qec;case 59:return Afc(),zdc;case 57:return Afc(),pdc;case 58:return Afc(), +oec}throw gZ(new qnb("Not an assignment op:"+a));}function yhc(a,b,c,d){var e,f,g,h,i,j,k;if(!!a.Li()&&joc(a.Li(),b))return Onc(a.ki(),b.ki(),c,d,0);k=skc(d,a,b);if(k)return k.a;i=Bhc(a);j=Bhc(b);if(!gh(j,i)){tkc(d,a,b,(Hkc(),Fkc));return false}for(h=(f=(new Yvc(i.a)).a.He().b.vj(),new ewc(f));h.a.Id();){g=(e=h.a.Jd(),e.Ve());if(!Qfc(b.Wi(g),a.Wi(g),c,d)){tkc(d,a,b,(Hkc(),Fkc));return false}}tkc(d,a,b,(Hkc(),Ekc));return true}function vhb(a,b,c){var d,e,f,g,h,i,j,k;k=g9b(b);d=g9b(b);i=g9b(b);f=d.c? +new pib(a.a):null;j=!i?null:new pib(a.a);g=Thb(a.a,c);Lhb(a.a,f,j,k);thb(a,k,null,null);if(!i)Shb(a.a,f,g,k);else{cib(a.a,j);Khb(a.a,f,j,i);thb(a,i,null,null);Rhb(a.a,g,i)}if(d.c){Xd(d.c.n==(Afc(),adc));cib(a.a,f);h=z8b(d.c.c);Jhb(a.a,j,h);e=z8b(d.c.c);Xd(e.n==Vcc);thb(a,e,null,null);Qhb(a.a,j,e)}cib(a.a,g)}function uq(a,b,c,d){var e,f,g,h,i;i=yZ(rZ(VLc,Vrc(yZ(rZ(b==null?0:Db(b),WLc)),15)));e=yZ(rZ(VLc,Vrc(yZ(rZ(c==null?0:Db(c),WLc)),15)));h=xq(a,b,i);g=wq(a,c,e);if(!!h&&e==h.a&&Gd(c,h.g))return c; +else if(!!g&&!d)throw gZ(new qnb("key already present: "+c));!!h&&pq(a,h);!!g&&pq(a,g);f=new $q(c,e,b,i);rq(a,f,g);if(g){g.e=null;g.c=null}if(h){h.e=null;h.c=null}vq(a);return!h?null:h.g}function LE(a,b,c,d,e,f){var g,h,i,j,k,l,m;j=OE(b)-OE(a);g=YE(b,j);i=HE(0,0,0);while(j>=0){h=RE(a,g);if(h){j<22?(i.l|=1<>>1;g.m=k>>>1|(l&1)<<21;g.l=m>>>1|(k&1)<<21;--j}c&&NE(i);if(f)if(d){EE= +XE(a);e&&(EE=_E(EE,(iF(),gF)))}else EE=HE(a.l,a.m,a.h);return i}function Wqb(a,b){ppb();var c,d,e,f,g,h,i;c=Hsc(b,_sc(46));if(c==-1)return Vqb(a,b);f=b.substr(0,c);Esc(FMc,f)?e=new J9b((Afc(),lfc)):Esc(DMc,f)?e=new J9b((Afc(),ffc)):e=Vqb(a,f);do{i=c+1;c=Isc(b,_sc(46),i);g=c==-1?b.substr(i):b.substr(i,c-i);h=(n8b(),new Wbc((Afc(),bfc),g));t9b(h,g.length);H6(a).Gg(g)&&b9b(h,a8b,true);d=e.e+1+g.length;e=x3b(e,h);e.e=d}while(c!=-1);return e}function PBb(a,b){var c,d,e,f,g,h,i,j;g=new CHc;for(f=(j=(new Wuc(qjc(b).a)).a.He().Vd(), +new avc(j));f.a.Id();){e=(c=f.a.Jd(),c.Ve());AHc(g,e.di(new stc,true).a)}for(i=(d=(new Yvc(g.a)).a.He().b.vj(),new ewc(d));i.a.Id();){h=(c=i.a.Jd(),c.Ve());a.a+=mRc;ptc(ptc(ptc(ptc((a.a+="@",a),NNc)," {"),h),"}");a.a+=gMc}b.f==(dkc(),bkc)&&b.e?(a.a+=" * @record\n",a):(a.a+=" * @interface\n",a)}function FVb(a){switch(DWb(a.g,0).e.f){case 76:case 29:case 9:case 99:case 14:case 2:case 108:case 109:case 110:case 111:case 64:case 69:case 18:case 97:case 100:case 45:case 47:case 49:case 63:case 68:case 94:case 95:case 101:case 107:case 104:case 35:case 21:case 77:case 98:case 24:case 26:case 44:return true; +case 34:return AVb(a,(AZb(),lYb))&&zVb(a,1,GYb);default:return false}}function o2b(a){var b,c;if(a.a==null)return j2b(a,jLc,0);if(pF(a.a,186))return j2b(a,etc(a.a),0);else if(pF(a.a,132)){b=Frc(a.a);if(!(b>=0&&b<=lMc))throw gZ(new _1b(b));c=b=0&&b<=lMc),b>=kMc?CE(xE(xF,1),eLc,46,15,[55296+(b-kMc>>10&mMc)&dLc,56320+(b-kMc&mMc)&dLc]):CE(xE(xF,1),eLc,46,15,[b&dLc])));return j2b(a,c,0)}else throw gZ(g2b(a));}function Ifc(a,b,c,d,e){var f,g,h,i,j,k;g=new kcc((Afc(), +Kdc),a);Sd(b);Sd(c);for(j=new TEc(new KEc(b));j.b!=j.c.a.b;){h=REc(j);f=K3b(h.d);q8b(g,Jfc(f,h.e))}for(i=new TEc(new KEc(c));i.b!=i.c.a.b;){h=REc(i);f=K3b(h.d);b9b(f,(n8b(),f8b),true);q8b(g,Jfc(f,h.e))}if(d!=null){k=new L9b(Sec,(ae(Hsc(d,_sc(46))==-1,HMc,d),n8b(),new Wbc(rec,d)));q8b(g,(!!e&&d9b(k,(Hbc(),Nac),e),k))}return g}function af(a){var b,c,d,e;c=a.c;while(a.c!=-1){e=c;d=a.le(a.c);if(d==-1){b=a.e.length;a.c=-1}else{b=d;a.c=a.ke(d)}if(a.c==c){++a.c;a.c>a.e.length&&(a.c=-1);continue}while(e< +b&&a.f.Md(zsc(a.e,e)))++e;while(b>e&&a.f.Md(zsc(a.e,b-1)))--b;if(a.d&&e==b){c=a.c;continue}if(a.b==1){b=a.e.length;a.c=-1;while(b>e&&a.f.Md(zsc(a.e,b-1)))--b}else--a.b;return Tsc(a.e,e,b)}return a.i=2,null}function P_(a,b){var c,d,e,f,g,h,i;switch(b.n.f){case 98:h=Cfc(b.n),h!=-1&&(d=C8b(b),h!=d&&D0(QMc+h+RMc+d,b));G_(a,b.c,true);break;case 159:e=C8b(b),0!=e&&D0(OMc+e,b);break;case 160:i=Cfc(b.n),i!=-1&&(f=C8b(b),i!=f&&D0(QMc+i+RMc+f,b));q_(b.c,0);break;case 161:g=Cfc(b.n),g!=-1&&(c=C8b(b),g!=c&&D0(QMc+ +g+RMc+c,b));break;default:D0("Interface contained member of invalid type "+b.n,b)}}function uab(){uab=JZ;gab=new xab(xOc,0);hab=new xab(yOc,1);iab=new xab("ECMASCRIPT5_STRICT",2);kab=new xab("ECMASCRIPT_2015",3);jab=new xab("ECMASCRIPT6_TYPED",4);lab=new xab("ECMASCRIPT_2016",5);mab=new xab("ECMASCRIPT_2017",6);nab=new xab(zOc,7);oab=new xab(AOc,8);pab=new xab("ECMASCRIPT_NEXT",9);rab=new xab("STABLE",10);qab=new xab("NO_TRANSPILE",11);tab=new xab(BOc,12);sab=nab}function cGb(a,b){var c,d,e,f,g,h, +i,j,k,l;j=new DHc(new hGb);for(e=a.Vd();e.Id();){d=e.Jd();for(g=b.Vd();g.Id();){f=g.Jd();if(Esc(d.substr(0,f.length),f)){d=Usc(d,f.length);break}}h=Rsc(d,"/node_modules/",0);i="";for(c=0;c +0){j[e]=k;break}else{h=i.index;j[e]=k.substr(0,h);k=Vsc(k,h+i[0].length,k.length);d.lastIndex=0;if(g==k){j[e]=k.substr(0,1);k=k.substr(1)}g=k;++e}}if(c==0&&a.length>0){f=j.length;while(f>0&&j[f-1]=="")--f;f36)throw gZ(new usc("radix "+b+" out of range"));e=a.length;f=e>0&&(a.charCodeAt(0)==45||a.charCodeAt(0)==43)?1:0;for(c=f;c_Kc)throw gZ(new usc(ITc+a+'"'));return g}function rMb(a, +b){var c,d,e,f,g,h;switch(b.p.f){case 33:c=b;f=c.c;e=c.b;d=true;break;case 34:h=b;f=h.d;e=h.b;d=true;break;case 1:g=b;f=g.k;e=g.i;d=g.f||g.e;break;default:return false}if(e)return false;if(f.e!=(AZb(),iYb)||!Esc(f.a,LMc))return false;if(d){a.a.d.Ah("Class constructor may not be getter, setter, async, or generator.",a.a.q,(RJb(),b.o.b.b+1),b.o.b.a);return false}return true}function Jjc(a,b,c){var d,e,f,g,h,i,j,k,l;Sd(b);e=new ukc(false);if(Qfc(a,b,0,e))return a;if(a.f==(dkc(),ckc)&&b.f==ckc&&!ehc(a.b)&& +!ehc(b.b)){i=a.zi(b);j=b.zi(a);if(i&&!j)return c?b:a;else if(j&&!i)return c?a:b;l=Kjc(a,b,c);if(l)return l}g=a.A.u[17];f=new ukc(false);if(Qfc(g,b,0,f))return c?b:a;else{d=new ukc(false);if(Qfc(g,a,0,d))return c?a:b}h=a.A.u[62];k=a.A.u[64];return c?h:k}function LWb(a,b,c){var d,e;e=false;while(xWb(a,a.f)&&(xWb(a,a.f)?zsc(a.b,a.f):0)!=c&&!mXb(xWb(a,a.f)?zsc(a.b,a.f):0)){d=xWb(a,a.f)?zsc(a.b,a.f):0;e=e||d==8232||d==8233;if(!aXb(a))return new tXb(Vsc(a.b,b,a.f),$Sb(a.i.b,b,a.f),e)}(xWb(a,a.f)?zsc(a.b, +a.f):0)!=c?EWb(a,ZSb(a.i.b,b),"Unterminated string literal",CE(xE(iW,1),mLc,1,5,[])):yWb(a);return new tXb(Vsc(a.b,b,a.f),$Sb(a.i.b,b,a.f),e)}function ZEb(){ZEb=JZ;new Mcb("JSC_MODULE_CONFLICT",(E2(),D2),new Rtc("File cannot be a combination of goog.provide, goog.module, and/or ES6 module: {0}"));YEb=new Mcb("JSC_JS_MODULE_LOAD_WARNING",B2,new Rtc('Failed to load module "{0}"'));XEb=new Mcb("JSC_INVALID_MODULE_PATH",B2,new Rtc('Invalid module path "{0}" for resolution mode "{1}"'));WEb=new aFb((Bn(), +Bn(),An),(null,An),(nEb(),mEb))}function OOb(a){var b,c,d,e;c=_Ib(gJb(dJb(eJb(bJb(hJb(aJb(fJb(cJb(gJb(dJb(new iJb,(zJb(),xJb)),(KJb(),JJb)),(mJb(),lJb)),(FJb(),EJb)),WIb((bn(),mz(),lz))),(null,lz)),(null,lz)),false),rJb),IJb));e=COb(a.b,a.f,c,a.a);b=e.a;if(b.n!=(Afc(),Xec)||b.c.n!=Ddc){a.a.Bh(URc+occ(kSc,CE(xE(iW,1),mLc,1,5,[lSc])),a.b.ug(),a.d,a.c);return false}d=b.c.c;if(!gPb(a,d))return false;KOb(a,d);a.e=d;return true}function gC(b,c,d){var e,f,g,h,i,j;!d&&(d=new kC);try{if(c.c>=0||c.d!=null|| +c.g!=null||c.e!=null)throw gZ(new rD("Invalid map format"));f=new PC;for(j=c.f.Vd();j.Id();){i=j.Jd();g=i.d;i.c==0&&(g=null);if(g==null)throw gZ(new rD("Unable to retrieve: "+i.d));NC(f,i.b,i.a,g)}h=new stc;KC(f,h,c.b);fC(b,h.a)}catch(a){a=fZ(a);if(pF(a,104)){e=a;throw gZ(new rD("IO exception: "+e));}else throw gZ(a);}}function dPb(a,b){var c,d;d=(Pd(b.n==(Afc(),Ycc),iSc,b),b.c.Wh());c=PPb($b(d));switch(c.f){case 19:return bPb(a,b);case 22:return jPb(a,b);case 11:return JOb(a,b,(NPb(),APb));case 0:return JOb(a, +b,(NPb(),pPb));case 23:return JOb(a,b,(NPb(),MPb));case 14:return ZOb(a,b);case 18:return cPb(a,b);case 17:return _Ob(a,b);case 20:return VOb(a,b);default:throw gZ(new Irc("Invalid type expression"));}}function RB(a,b,c){var d,e,f,g,h,i;Sd(c);if(b==0)throw gZ(new Dqc("/ by zero"));f=a/b|0;h=a-b*f;if(h==0)return f;i=1|(a^b)>>31;switch(c.f){case 7:UB(h==0);case 1:g=false;break;case 0:g=true;break;case 2:g=i>0;break;case 3:g=i<0;break;case 6:case 5:case 4:d=$wnd.Math.abs(h);e=d-($wnd.Math.abs(b)-d); +e==0?g=c==(Itc(),Ftc)||c==Etc&(f&1)!=0:g=e>0;break;default:throw gZ(new Jqc);}return g?f+i:f}function Z6(a){var b,c,d,e,f;(new puc(a.u.a)).a.Rd();for(e=new wxc(a.k);e.a=0,"invalid depth: %s",e);f.b=e;e==h.a.length&&Ywc(h,new fxc);h.a[e].Pd(f)}return h}function vqb(a){ppb();var b;switch(a.n.f){case 29:case 26:case 27:break;default:return false}b=a.g;if(!b)return false;switch(b.n.f){case 107:return(b.c? +b.c.i:null)==a;case 76:case 100:case 88:case 114:case 45:case 105:case 63:case 64:case 78:return true;case 96:case 65:case 121:case 72:case 73:case 101:case 102:return b.c==a;case 93:case 91:case 116:return xqb(a);default:return aqb(b)&&b.c==a}}function uHb(){uHb=JZ;sHb=new Mcb("JSC_NULLABLE_RETURN",(E2(),C2),new Rtc("This function''s return type is nullable, but it always returns a non-null value. Consider making the return type non-nullable."));tHb=new Mcb("JSC_NULLABLE_RETURN_WITH_NAME",C2,new Rtc('The return type of the function "{0}" is nullable, but it always returns a non-null value. Consider making the return type non-nullable.'))} +function Khb(a,b,c,d){var e,f,g;$hb(a,b,c);f=Ohb(a);g=swc(a.k)?null:qwc(a.k);e=new fxc;if(a.o==0){if(!!f||!!g){Ywc(e,!f?F9b(Yjb((n8b(),new tac(0)),a.r.f.e),d):mib(f,d));!!g&&Ywc(e,mib(g,d))}}else{Ywc(e,!f?F9b(Yjb((n8b(),new tac(0)),a.r.f.e),d):mib(f,d));Ywc(e,!g?F9b(Yjb((n8b(),new tac(0)),a.r.f.e),d):mib(g,d));Ywc(e,F9b(O3b(a.o),d))}eib(a,Dhb(a,d,"enterFinallyBlock",exc(e,zE(HU,IMc,7,0,0,1))));++a.o}function Msb(){Msb=JZ;Jsb=new Mcb("JSC_FEATURES_NOT_SUPPORTED_BY_PASS",(E2(),B2),new Rtc('Attempted to run pass "{0}" on input with features it does not support. {1}\nUnsupported features: {2}')); +Lsb=RIc((drc(EO),EO.p));Ksb=(Bn(),new hz(Qn(CE(xE(iW,1),mLc,1,5,["inlineFunctions",uOc,"deadAssignmentsElimination",tOc,"removeUnusedCode",wOc,vOc,FQc,GQc]))));Isb=new hz(Qn(CE(xE(iW,1),mLc,1,5,[FQc,GQc])))}function DLb(a,b){var c,d,e,f,g,h,i,j;f=new tEc;c=new tEc;g=null;h=null;if(WKb(a,b.a.a))for(e=b.a.a.Vd();e.Id();){d=e.Jd();i=null;if(d.p==(n1b(),a1b)){j=d;i=yKb(a.a,j.b);d=j.a}switch(d.p.f){case 26:qEc(f,d.a.a,i);break;case 81:gLb(a,d,(IRb(),qRb));qEc(c,d.a.a.a,i);break;case 65:g=d.a.a.a;h=i;break; +default:throw gZ(new Irc("Illegal parameter type: "+d.p));}}return SJb(a.a,Ifc(yKb(a.a,b.b),f,c,g,h))}function F6(a,b,c,d,e,f,g,h){var i,j,k,l,m,n;if(!d.Wd(b)){c&&b.j==(b9(),a9)&&D8(b,(b9(),$8));return}j=new Ckb(a,g,h);Bkb(j,Sd(x8(b,a)));c&&b.j==(b9(),a9)&&D8(b,(b9(),$8));_tc(a.D,UFb(z8(b).a),b.j);i=Dm(CE(xE(cW,1),mLc,15,0,[xDb(A8(b)),In(b.d),y8(b).i]));for(n=new Tu(new Lm(i.a.length,i.a));Ru(n);){m=Su(n);l=null;k=false;if(f.se(m))l=f.Ie(m);else if(e.se(m)){k=true;l=e.Ie(m)}!!l&&F6(a,l,k,d,e,f,g, +h)}}function rjc(a,b){var c,d,e,f,g,h,i,j;g=!b;d=Hic(Dic(new Lic(a.A),b?a.b.b:(c=Hic(Dic(new Lic(a.A),a.b.b),a.B.c),uic(c))),a.B.c);h=a.b.a;if(h){i=x8b(h,false);j=(n8b(),new Wbc((Afc(),rec),"thisType"));r9b(j,Skc(a.A,Yfc(a.k)?a.A.u[51]:a.k));r8b(i,j);if(g)for(e=j.f;e;e=e.f)c9b(e,(Hbc(),qbc),1);else if(b){f=j.f;(!f||E8b(f,(Hbc(),qbc))!=0||E8b(f,(Hbc(),Ebc))!=0)&&c9b(j,(Hbc(),qbc),1)}d.d=i}return uic(d)}function Trb(a,b){var c,d,e,f,g,h;Yd(a.n==(Afc(),Jdc),a);ppb();if(a.n==Jdc&&iqb(a.g)&&a.n==Jdc&& +Gqb(a.c)&&!sqb(a)){e=a.c;f=e.Sh(false);g=F9b(r3b(f,ufc),a);e._h("");s7(b,G6(e));i7(b);h=a.g;e9b(h,a);r8b(h,g);s7(b,G6(g));i7(b);r8b(f,a);return true}else if(a.n==Jdc&&(Ld(a.n==Jdc,a),a.c?a.c.i:null).n!=Vcc){d=(Ld(a.n==Jdc,a),a.c?a.c.i:null);c=j3b(S3b(z8b(d)));I9b(c,d);q8b(a,c);s7(b,G6(c));i7(b)}return false}function h0(a,b,c){var d,e,f;F_(a,(IRb(),lRb),c);b0((Afc(),Fec),c);for(d=c.c;d;d=d.f)switch(d.n.f){case 91:i0(a,b,d);break;case 114:F_(a,nRb,d);m0(a,b,d);break;case 116:F_(a,PQb,d);b0(fdc,d);e= +Cfc(d.n);e!=-1&&(f=C8b(d),e!=f&&D0(QMc+e+RMc+f,d));E_(a,d.c);(d.c?d.c.i:null).n==mdc?x_(a,b,d.c?d.c.i:null):R_(a,(d.c?d.c.i:null).n,d.c?d.c.i:null);break;default:D0("Invalid object pattern child for "+b+TMc,c)}}function UTb(a){var b,c,d,e,f;c=false;d=(Bn(),new Zr);while(true){e=DWb(a.g,0).d.b;switch(DWb(a.g,0).e.f){case 4:iTb(a,(AZb(),GXb));b=xTb(a,1);iTb(a,NXb);f=kVb(a);Xr(d,new WZb(new Z1b(e,a.f),b,f));break;case 8:c?VVb(a,"Switch statements may have at most one default clause",CE(xE(iW,1),mLc, +1,5,[])):c=true;iTb(a,(AZb(),TXb));iTb(a,NXb);Xr(d,new s$b(new Z1b(e,a.f),kVb(a)));break;default:return In(d.a)}}}function C2b(a,b){var c,d,e;e=a.b;c=a.b=48&&c<=57){d=B2b(a);if((a.b=48&&c<=57?F2b(a,b,B2b(a)):c==46?E2b(a, +b):D2b(a,b)}function Gbb(a){var b,c,d;if("".length>0){if(!Kbb(a.a,"".charCodeAt(0))){b=zE(xF,eLc,46,a.a.length,15,1);for(c=0;c";case 5:return"[";case 7:return"{";case 9:return"(";case 2:return"<";case 17:return"?";case 14:return"|";case 6:return"]";case 8:return"}";case 10:return")";case 15:return"*";case 18:return HNc;case 20:return"=";case 4:return a.j.n;default:throw gZ(new Irc(b.e!= +null?b.e:""+b.f));}}function Hlb(a,b){var c,d,e;switch(b.g.f){case 0:!a.d&&(a.d=b);!a.f&&(a.f=Jlb(b));++a.i;break;case 1:++a.j;c=!b.c?null:(ppb(),d=Dpb(b.c),!d?null:H8b(d,(Hbc(),mbc)));!!c&&(c.a&hQc)!=0&&++a.k;break;case 2:case 4:e=b.c;!a.g&&!!e&&e.n==(Afc(),Pdc)&&e.g.n==(Afc(),Ddc)&&(a.g=H8b(e,(Hbc(),mbc)));++a.s;break;case 3:++a.a;++a.s;break;case 5:++a.c;++a.s;break;case 6:++a.e;break;case 7:++a.r;++a.s;break;default:throw gZ(new Hrc);}}function WTb(a,b,c){var d,e,f,g,h,i,j,k;h=DWb(a.g,0).d.b; +iTb(a,(AZb(),IXb));g=null;(!b||(k=DWb(a.g,0).e,iYb==k||a.b.d&&VSb(k)||!a.b.c&&USb(k)))&&(g=jTb(a));e=tTb(a);i=null;if(AVb(a,cYb)){iTb(a,cYb);i=xTb(a,1)}f=(Bn(),new Zr);if(a.b.d&&AVb(a,kYb)){iTb(a,kYb);j=nVb(a);Ywc(f.a,Sd(j));while(AVb(a,OXb)){iTb(a,OXb);j=nVb(a);!!j&&(Ywc(f.a,Sd(j)),f)}}iTb(a,FYb);d=YTb(a,c);iTb(a,KXb);return new YZb(new Z1b(h,a.f),g,e,i,In(f.a),d)}function G_(a,b,c){var d,e,f,g,h,i,j,k;b0((Afc(),Jdc),b);f=Cfc(b.n);f!=-1&&(e=C8b(b),f!=e&&D0(QMc+f+RMc+e,b));k0(a,b.c.f);g=b.c;d=b.c? +b.c.i:null;if(b.n==Jdc&&E8b(b,(Hbc(),Fac))!=0){b0(rec,g);c0(g)&&g.Wh().length!=0&&D0("Expected empty string.",g);h=Cfc(g.n);h!=-1&&(i=C8b(g),h!=i&&D0(QMc+h+RMc+i,g));d.n==Vcc?n_(a,d):E_(a,d)}else{b0(rec,g);c0(g);j=Cfc(g.n);j!=-1&&(k=C8b(g),j!=k&&D0(QMc+j+RMc+k,g));c?b0(tdc,d):n_(a,d)}H_(a,b)}function cPb(a,b){var c;if(!JOb(a,b,(NPb(),HPb)))return false;if(!gPb(a,(Pd(b.n==(Afc(),Ycc),iSc,b),B8b(b,1)))){mPb(a,HPb.d);return false}if((Pd(b.n==Ycc,iSc,b),B8b(b,2)).n!=Cec){a.a.Bh(URc+occ(jSc,CE(xE(iW,1), +mLc,1,5,[qLc])),a.b.ug(),a.d,a.c);mPb(a,HPb.d);return false}c=(Pd(b.n==Ycc,iSc,b),B8b(b,2)).Uh();if(c<0||c%1!=0){a.a.Bh(URc+occ(jSc,CE(xE(iW,1),mLc,1,5,[qLc])),a.b.ug(),a.d,a.c);mPb(a,HPb.d);return false}return true}function N9b(a,b,c){n8b();Ld(!b.g,"first new child has existing parent");Ld(!b.f,"first new child has existing next sibling");Ld(!b.i,"first new child has existing previous sibling");Ld(!c.g,"second new child has existing parent");Ld(!c.f,"second new child has existing next sibling"); +Ld(!c.i,"second new child has existing previous sibling");this.n=a;this.g=null;this.c=b;b.f=c;b.i=c;b.g=this;c.f=null;c.i=b;c.g=this;this.k=-1}function Dpb(a){ppb();var b,c;if(a.n==(Afc(),Ddc))return Dpb(a.c);b=H8b(a,(Hbc(),mbc));if(!b){c=a.g;if(!c||a.n==Ddc)return null;if(c.n==rec)return Dpb(c);else if(c.n==Ccc)return Dpb(c);else if(Sqb(c)||c.n==fdc)return c;else if((c.n==Jdc||c.n==bdc)&&a==c.c)return Dpb(c);else if(!!c&&(c.n==ufc||c.n==jec||c.n==gdc)&&!!c.c&&!c.c.f)return c;else if(c.n==Sdc&&c.c!= +a||c.n==Hec||c.n==wcc||c.n==edc&&c.c!=a)return Dpb(c)}return a}function Mub(a,b,c){var d,e,f,g;Sd(c);Xd(c.n==(Afc(),zfc));Xd(!!b&&!!b.a);Xd(S8b(b.a));d=g9b(c);e=(g=M3b(b_((Kob(a),NQc)),CE(xE(HU,1),IMc,7,0,[])),g);if(E8b(c,(Hbc(),Gbc))!=0){Sd(d);q8b(e,b_((Kob(a),"$jscomp.AsyncGeneratorWrapper$ActionEnum.YIELD_STAR")));q8b(e,d)}else{!d&&(d=(ppb(),f=$3b((n8b(),new tac(0))),f));q8b(e,b_((Kob(a),"$jscomp.AsyncGeneratorWrapper$ActionEnum.YIELD_VALUE")));q8b(e,d)}I9b(e,c);r8b(c,e);h9b(c,(n8b(),m8b))}function Mtb(a, +b){Itb();if(a.n==(Afc(),Ycc)&&!!a.c&&!!a.c.f&&a.c.f==(a.c?a.c.i:null))if(b==(NFb(),MFb)&&(_8b(a.c,"__webpack_require__")||_8b(a.c,"__webpack_require__.t"))&&(a.c.f.n==Cec||a.c.f.n==bfc))return true;else{if(_8b(a.c,lNc)&&a.c.f.n==bfc)return true}else if(a.n==Ycc&&C8b(a)==3&&b==(NFb(),MFb)&&_8b(a.c,"__webpack_require__.bind")&&a.c.f.n==Aec&&((a.c?a.c.i:null).n==Cec||(a.c?a.c.i:null).n==bfc))return true;return false}function Ntb(a){var b,c,d,e;Kd(a.n==(Afc(),Jdc));if(!a.g)return false;d=a.g;if(d.n!= +Ycc)return false;if(!(!!d.c&&d.c.n==Pdc&&d.c.c.n==Ycc))return false;e=d.c.c.c;if(a9b(e,JQc,JQc.length)&&d.c.c.f.n==bfc&&Esc(d.c.c.f.Wh(),"then"))return true;else if(a9b(e,KQc,KQc.length)&&!!e.f&&e.f.n==zcc){b=false;for(c=e.f.c;c;c=c.f){if(!(c.n==Ycc&&!!c.c&&!!c.c.f&&c.c.f==(c.c?c.c.i:null)&&_8b(c.c,JQc)))return false;b=true}return b}return false}function Awb(a,b){var c,d,e,f,g,h,i;if(!b)return!a?0:1;if(a.b!=b.b)return ee(b.b,a.b);g=a.a.f;h=b.a.f;if(g!=null&&h!=null){i=Asc(g,h);if(i!=0)return i}else if(g== +null&&h!=null)return-1;else if(g!=null&&h==null)return 1;e=a.a.d;f=b.a.d;if(e!=f)return e-f;else if(e<0&&0<=f)return-1;else if(0<=e&&f<0)return 1;c=a.a.a;d=b.a.a;if(c!=d)return c-d;else if(c<0&&0<=d)return-1;else if(0<=c&&d<0)return 1;return Asc(a.a.c,b.a.c)}function ROb(a,b){if(!JOb(a,b,(NPb(),qPb)))return false;if(!POb(a,(Pd(b.n==(Afc(),Ycc),iSc,b),B8b(b,1)))){a.a.Bh(URc+occ(mSc,CE(xE(iW,1),mLc,1,5,[nSc])),a.b.ug(),a.d,a.c);return false}if(!gPb(a,(Pd(b.n==Ycc,iSc,b),B8b(b,2)))){a.a.Bh(URc+occ(mSc, +CE(xE(iW,1),mLc,1,5,[nSc])),a.b.ug(),a.d,a.c);return false}if(!gPb(a,(Pd(b.n==Ycc,iSc,b),B8b(b,3)))){a.a.Bh(URc+occ(mSc,CE(xE(iW,1),mLc,1,5,[nSc])),a.b.ug(),a.d,a.c);return false}return true}function hdb(a,b,c,d){var e,f,g,h,i;Yd(c.n==(Afc(),Pdc)||c.n==Odc,c);Yd(b.n==ffc,b);g=c.g;if(vqb(c)){u7(a.a,Lmb(c,(Rjb(),Qjb),CE(xE(nW,1),uNc,2,6,["assigning to a super property"])));return}e=(ppb(),Gpb(b,new frb(bdc)));i=e.c.f;if(!V8b(i))return;if(E8b(d,(Hbc(),zbc))!=0){f=G9b(x8b(i,false),b);d9b(f,sbc,DMc);j9b(b.g, +b,f)}else{h=Mc(new Pc(String.fromCharCode(46)),I8b(i),aNc,CE(xE(iW,1),mLc,1,5,[]));k9b(b,G9b(Xqb(a.a,h,b),b))}w7(a.a,g)}function WMb(a,b){var c;if(b==(fOb(),dOb))return QMb(a,(Afc(),afc));else if(b==WNb){xNb(a);return iNb(a,!a.o?mOb(a.j):GMb(a))}else if(b==XNb){xNb(a);return sNb(a,!a.o?mOb(a.j):GMb(a),null)}else if(b==eOb){c=a.j.n;switch(c){case ZKc:xNb(a);return aNb(a,!a.o?mOb(a.j):GMb(a));case jLc:case vPc:return RMb(a,c,a.j.g,lOb(a.j));case yQc:return rNb(a,!a.o?mOb(a.j):GMb(a));default:return pNb(a, +b)}}a.o=b;return CMb(a,_Rc,a.j.g,lOb(a.j)),null}function eNb(a,b){var c,d;if(b==(fOb(),WNb)){c=new mDc;while(true){if(OMb(a,eOb)){d=a.j.n;!vMb.Sd(d)&&!l6b(a.f,d)&&BMb(a,"msg.jsdoc.modifies.unknown",d,a.j.g,lOb(a.j));jDc(c,a.j.n);b=!a.o?mOb(a.j):GMb(a)}else{zMb(a,dSc,a.j.g,lOb(a.j));return b}if(OMb(a,ZNb))b=!a.o?mOb(a.j):GMb(a);else break}if(OMb(a,aOb)){b=!a.o?mOb(a.j):GMb(a);Y6b(a.f,c)||zMb(a,"msg.jsdoc.modifies.duplicate",a.j.g,lOb(a.j))}else zMb(a,dSc,a.j.g,lOb(a.j))}return b}function fhc(a,b,c, +d){var e,f,g,h,i,j,k,l,m,n;if(!pF(b,213))return false;f=b;if(!a.b.Ai(f.b,c,d))return false;m=a.a.c;i=f.a.c;while(!!m&&!!i){n=m.d;j=i.d;if(n)if(!j||!j.Ai(n,c,d))return false;l=E8b(m,(Hbc(),Ebc))!=0;h=E8b(i,Ebc)!=0;k=l||E8b(m,qbc)!=0;g=h||E8b(i,qbc)!=0;if(!k&&g){e=h&&(!j||j.Bi()||j.ti());if(!e)return false}l||(m=m.f);h||(i=i.f);if(l&&h){m=null;i=null}}return!m||E8b(m,(Hbc(),qbc))!=0||E8b(m,(Hbc(),Ebc))!=0||!!i}function kNb(a,b){var c,d;if(b!=(fOb(),WNb)){zMb(a,eSc,a.j.g,lOb(a.j));return b}else{d=new mDc; +while(true){if(OMb(a,eOb)){c=a.j.n;a.k.Sd(c)||BMb(a,"msg.jsdoc.suppress.unknown",c,a.j.g,lOb(a.j));jDc(d,a.j.n);b=!a.o?mOb(a.j):GMb(a)}else{zMb(a,eSc,a.j.g,lOb(a.j));return b}if(PMb(a,ZNb,PNb))b=!a.o?mOb(a.j):GMb(a);else break}if(OMb(a,aOb)){b=!a.o?mOb(a.j):GMb(a);n7b(a.f,d)}else zMb(a,eSc,a.j.g,lOb(a.j));return IMb(a,!a.o?mOb(a.j):GMb(a))}}function p1b(){n1b();return CE(xE(pT,1),sLc,19,0,[N0b,m0b,G_b,j1b,h1b,b0b,f0b,r0b,__b,k1b,j0b,l0b,i1b,X_b,H_b,R0b,l1b,K_b,Z_b,V0b,w0b,$0b,L_b,_0b,Y_b,Z0b,q0b, +x0b,A_b,I0b,O_b,Q_b,P_b,p0b,S0b,O0b,R_b,S_b,U_b,V_b,B0b,N_b,F_b,W_b,e1b,g1b,y0b,F0b,z_b,I_b,M_b,v0b,D0b,E0b,c0b,z0b,M0b,g0b,U0b,B_b,D_b,J0b,h0b,T0b,H0b,Q0b,C0b,d0b,e0b,s0b,t0b,k0b,i0b,m1b,$_b,W0b,X0b,Y0b,c1b,d1b,a1b,K0b,L0b,C_b,P0b,f1b,n0b,o0b,A0b,T_b,b1b,y_b,u0b,J_b,G0b,E_b,a0b])}function S2(){S2=JZ;R2=new Mcb("JSC_REGEXP_REFERENCE",(E2(),D2),new Rtc("References to the global RegExp object prevents optimization of regular expressions."));Q2=new Mcb("JSC_MALFORMED_REGEXP",D2,new Rtc("Malformed Regular Expression: {0}")); +tp("$1","$2","$3","$4","$5","$6",CE(xE(nW,1),uNc,2,6,["$7","$8","$9","$_","$input","input","lastMatch","lastParen","leftContext","rightContext",vNc,"ignoreCase","lastIndex","multiline","source"]))}function _6(a,b){var c,d;a.G=b;z7(a,wab(b.Sb));!a.g&&x7(a,new Znb((c=a.G.K,d=new Wnb(a),d.d=c,d),u6));a.C=(ZEb(),WEb);j9(a.G,(Hcb(),mcb))?a.G.w=true:i9(a.G,mcb)?a.G.w=false:a.G.w||h9(a.G,new gcb(ccb((kwb(),gwb)),(E2(),C2)));a.G.r!=(E2(),C2)&&!i9(a.G,scb)&&h9(a.G,new gcb(scb,a.G.r));k9(a.G)&&h9(a.G,new gcb(pcb, +B2));!a.G.v&&!j9(a.G,ncb)&&h9(a.G,new gcb(ncb,C2));a.G.ad&&!j9(a.G,ncb)&&h9(a.G,new gcb(ncb,C2));b.w||(b.ud=false);a7(a,b.wd)}function sfb(a,b,c,d){var e,f,g,h,i,j,k,l,m,n;k=Kob(b);j=c.g;if((c.n==(Afc(),jec)||c.n==gdc)&&!d.c&&(!j||j.n!=Hdc)&&(e=Gpb(c,hfb),!!e&&e.n!=Jdc)){m=F9b((n=(ae(Hsc(vPc,_sc(46))==-1,HMc,vPc),n8b(),new Wbc(rec,vPc)),a.d&&r9b(n,U6(a.a).u[52]),n),d);r8b(d,m);w7(a.a,m)}h=d.Wh();f=e$(k);if(k!=f){g=h;if(k$(f,h)||kDc(a.e,h)){do g=h+"$"+(""+(new j8(a.a)).a.Q++);while(k$(f,g));d._h(g); +w7(a.a,d);l=k.d;Vp(a.c,l,h,g)}i=i$(k,h);l$(k,i);twb(f,g,d,i.a)}}function gMb(a,b){var c,d;if(b.a.Yd()==1){c=b.a.ce(0);switch(c){case "any":d=SJb(a.a,new jcc((Afc(),ycc)));break;case YKc:d=SJb(a.a,new jcc((Afc(),Dec)));break;case XKc:d=SJb(a.a,new jcc((Afc(),Wcc)));break;case $Kc:d=SJb(a.a,new jcc((Afc(),dfc)));break;case XNc:d=SJb(a.a,new jcc((Afc(),wfc)));break;case vPc:d=SJb(a.a,new jcc((Afc(),rfc)));break;default:d=SJb(a.a,Kfc(b.a))}}else d=SJb(a.a,Kfc(b.a));T9b(d.k)==-1&&vKb(d,b.o.b,b.o.a);return d} +function NVb(a){switch(DWb(a.g,0).e.f){case 45:case 25:case 30:case 52:case 15:case 10:case 27:case 13:case 6:case 3:case 19:case 28:case 20:case 22:case 23:case 7:case 44:case 2:case 108:case 109:case 110:case 111:case 21:case 29:case 35:case 100:case 101:case 107:case 104:case 97:case 98:case 94:case 95:case 99:case 49:case 47:case 18:case 9:case 26:case 24:case 68:case 69:case 63:case 64:case 77:case 76:return true;default:return false}}function gPb(a,b){var c,d;if(!(b.n==(Afc(),rec)||b.n==bfc|| +b.n==Ycc)){a.a.Bh(URc+occ(kSc,CE(xE(iW,1),mLc,1,5,[lSc])),a.b.ug(),a.d,a.c);return false}if(b.n==rec||b.n==bfc)return true;d=(Pd(b.n==Ycc,iSc,b),b.c.Wh());if(!LOb(d)){a.a.Bh(URc+occ(kSc,CE(xE(iW,1),mLc,1,5,[lSc])),a.b.ug(),a.d,a.c);return false}c=PPb($b(d));switch(c.a){case 0:return dPb(a,b);case 1:return WOb(a,b);default:throw gZ(new Irc("Invalid type transformation expression"));}}function j2b(a,b,c){var d,e,f,g,h,i,j,k,l;j=c;k=a.b.p;h=a.b.n;d=btc(b);if(h>=0){d=$wnd.Math.min(d,h);pF(b,30)?(l=b.a.length, +dl&&(b.a+=ftc(zE(xF,eLc,46,d-l,15,1))),undefined):b=ctc(b,0,d)}k>0&&(k=$wnd.Math.max(btc(b),k));if(d>=k)return b;e=32;a.b.k?a.b.b==100?e=48:e=48:j=0;f=zE(xF,eLc,46,k-d,15,1);Lxc(f,f.length,e);g=a.b.f;i=pF(b,30)?b:new utc(b);g?(i.a+=gtc(f,0,f.length),i):rtc(i,j,gtc(f,0,f.length));return i}function U2b(a){var b,c,d,e,f,g,h,i;h=Rsc(a,"\r?\n",0);b=(oo(),new hs);for(e=0;e=100)while(lZ(rZ(kZ(d,10),mZ($wnd.Math.pow(10,c+1))),g)){d=kZ(d,10);++c}if(c>2)L3(a,zZ(d)+"E"+c);else{h=iZ(g,0)<0?sZ(g):g;iZ(h,1E12)>0&&gsc(h,4).length+2<(""+zZ(h)).length?L3(a,(iZ(g,0)<0?"-":"")+"0x"+gsc(h,4)):L3(a,""+zZ(g))}}else L3(a,Qsc(Osc(""+b,".0E","E"),"^(-?)0\\.","$1."))}function TLb(a,b){var c,d,e,f,g,h,i,j,k;switch(b.p.f){case 74:return d=uLb(a,b),e=d.c,f=iKb(a.a,(Afc(),cfc),e.Wh()), +wKb(f,e),c9b(f,(Hbc(),kbc),1),q8b(f,d),f;case 35:return ULb(a,b);case 36:c=b;return bKb(a.a,c,(IRb(),PQb)),g=yKb(a.a,c.a),h=c.b,i=(h.p==(n1b(),$_b)?j=uLb(a,h):h.p==q0b?j=MLb(a,h.a):j=AKb(a.a,h),j),k=fKb(a.a,(Afc(),fdc),g,i),T9b(k.k)==-1&&vKb(k,c.o.b,c.o.a),k;default:Yd(b.p==(n1b(),D_b),b);bKb(a.a,b,(IRb(),nRb));return lLb(a,b)}}function $Tb(a,b,c){var d,e,f,g,h,i;f=DWb(a.g,0).d.b;iTb(a,b);d=(Bn(),new Zr);while(AVb(a,(AZb(),fYb))||AVb(a,jYb))AVb(a,fYb)?Xr(d,(g=DWb(a.g,0).d.b,iTb(a,fYb),iTb(a,GYb), +h=DWb(a.g,0).e,iYb==h||a.b.d&&VSb(h)||!a.b.c&&USb(h)?wUb(a):UUb(a,1),oTb(a,"of"),KTb(a,1),iTb(a,LXb),new h$b(new Z1b(g,a.f)))):Xr(d,(i=DWb(a.g,0).d.b,iTb(a,jYb),iTb(a,GYb),KTb(a,1),iTb(a,LXb),new i$b(new Z1b(i,a.f))));KTb(a,1);iTb(a,c);return new j$b((e=new Z1b(f,a.f),In(d.a),e))}function zWb(a){var b;a.f=a.d.a.length==0?a.f:DWb(a,0).d.b.c;a.d.a=zE(iW,mLc,1,0,5,1);b=a.f;yWb(a);if(!XWb(a))return new dTb((AZb(),VYb),Vsc(a.b,b,a.f),$Sb(a.i.b,b,a.f));if((xWb(a,a.f)?zsc(a.b,a.f):0)!=47){FWb(a,"Expected '/' in regular expression literal", +CE(xE(iW,1),mLc,1,5,[]));return new dTb((AZb(),VYb),Vsc(a.b,b,a.f),$Sb(a.i.b,b,a.f))}yWb(a);while(hXb(xWb(a,a.f)?zsc(a.b,a.f):0))yWb(a);return new dTb((AZb(),VYb),Vsc(a.b,b,a.f),$Sb(a.i.b,b,a.f))}function oXb(b){var c,d,e,f;while(b.indexOf("\\")!=-1){e=Hsc(b,_sc(92));try{if(b.charCodeAt(e+1)!=117)return null;if(b.charCodeAt(e+2)!=123){d=e+6;f=b.substr(e+2,d-(e+2))}else{d=e+3;while(Uqc(b.charCodeAt(d),16)>=0)++d;if(b.charCodeAt(d)!=125)return null;f=b.substr(e+3,d-(e+3));++d}c=Crc(f,16)&dLc;if(!hXb(c))return null; +b=b.substr(0,e)+String.fromCharCode(c)+b.substr(d)}catch(a){a=fZ(a);if(pF(a,82)||pF(a,327))return null;else throw gZ(a);}}return b}function I4(a,b,c){switch(b.n.f){case 96:case 151:case 154:case 162:S3(a.c,c==(_4(),$4));break;case 65:(b.c?b.c.i:null).n==(Afc(),tdc)?a.c.Wg(true):a.c.Tg(c==(_4(),$4));break;case 158:b.g.n!=(Afc(),uec)&&I4(a,b.c,c);break;case 109:b.g.n!=(Afc(),uec)&&b.c.n!=kdc&&I4(a,b.c,c);break;case 116:!!b.c&&!b.c.f&&a.c.Wg(true);break;case 98:case 86:case 87:F8b(b.c).n==(Afc(),tdc)&& +a.c.Wg(true);break;case 159:a.c.Wg(true);break;default:c==(_4(),$4)&&a.c.Wg(false)}}function XMb(a,b){var c;if(b!=(fOb(),WNb)){zMb(a,"msg.jsdoc.missing.lc",a.j.g,lOb(a.j));return b}else if(OMb(a,eOb)){c=a.j.n;a.b.Sd(c)?u6b(a.f,c)||zMb(a,"msg.jsdoc.closurePrimitive.extra",a.j.g,lOb(a.j)):BMb(a,"msg.jsdoc.closurePrimitive.invalid",c,a.j.g,lOb(a.j));b=!a.o?mOb(a.j):GMb(a)}else{zMb(a,"msg.jsdoc.closurePrimitive.missing",a.j.g,lOb(a.j));return b}OMb(a,aOb)?b=!a.o?mOb(a.j):GMb(a):zMb(a,YRc,a.j.g,lOb(a.j)); +return IMb(a,!a.o?mOb(a.j):GMb(a))}function KC(a,b,c){var d,e,f,g,h,i,j;g=OC(a)+1;b.a+="{\n";SC(b,"version",true);b.a+="3";c!=null&&RC(b,(uD(),xD(c)));SC(b,"lineCount",false);b.a+=""+g;SC(b,"mappings",false);UC(new WC(a,b,g));SC(b,"sources",false);b.a+="[";QC(b,a.k);b.a+="]";JC(a,b);SC(b,"names",false);b.a+="[";QC(b,a.g);b.a+="]";for(f=(i=(new Wuc(a.a)).a.He().Vd(),new avc(i));f.a.Id();){e=(d=f.a.Jd(),d.Ve());h=pEc(a.a,e);tF(h)?j=(uD(),xD(h)):j=NZ(h);SC(b,e,false);b.a+=""+j}b.a+="\n}\n"}function mlb(a, +b){var c,d,e;e=a;for(d=new iac(new eac(Sd(a.g)));d.a.a;){c=hac(d);switch(c.n.f){case 41:case 84:case 76:case 100:case 88:case 66:case 70:case 72:case 73:case 25:case 79:case 19:case 20:case 21:case 22:return cmb(),Zlb;case 60:if(c.c==e)return cmb(),Zlb;break;case 47:if(!_8b(c.c,b))return cmb(),Wlb;break;case 29:if(!Esc(b,c.Wh()))return cmb(),Wlb;break;case 28:if(c.c!=e)return cmb(),Wlb;break;case 24:return cmb(),Ylb}e=c}return cmb(),Wlb}function Ojb(a,b,c){var d,e,f,g,h,i,j,k;j=U6(a.c);k=Wjb(c,j, +45);i=C8b(b);if(i==0)k9b(b,Yjb((n8b(),new Wbc((Afc(),bfc),'""')),k));else{f=g9b(b);Xd(f.n==(Afc(),jfc)&&f.Th()!=null);g=Yjb(V3b(f.Th()),k);if(i==1)j9b(b.g,b,g);else{d=Yjb(e3b(g,g9b(g9b(b))),b.d);for(h=2;h2;g=0;for(i=(f=(new Yvc(j.a)).a.He().b.vj(),new ewc(f));i.a.Id();){h=(e=i.a.Jd(),e.Ve());g>0&&(b.a+=",",b);k?(b.a+="\n ",b):g>0&&(b.a+=" ",b);ptc((b.a+=""+h,b),": ");Pfc(a.Wi(h),b,c);++g;if(!c&&g==10){b.a+=", ...";break}}k&&(b.a+=gMc,b);b.a+="}";a.s=true;return b}function Nvb(a){Kvb();var b,c,d,e,f,g,h,i;e=new Ns;g=(oo(),new hs);for(c=mf(Se(Re(Ye(new nc(10))),a));Pb(c);){b=Qb(c);i=Te(Re(Ye(new nc(32))),Ysc(b));if(i.b.Yd()==1&&i.a.ce(0).length== +0)continue;else if(i.b.Yd()<3)throw gZ(new qnb("Invalid table: too few tokens on line: "+b));h=i.a.ce(0);f=new Ivb(BQb(i.a.ce(1)),BQb(i.a.ce(2)),i.b.Yd()>3?i.a.ce(3):"");h.indexOf(gQc)!=-1?Ms(e,Psc(h,".*\\.prototype\\.",""),f):(Ywc(g.b,(el(h,f),new Wq(h,f))),g)}return new Mvb((d=new KEc(e.a),sm(d)),Co(g.b))}function RUb(a,b){var c,d,e,f,g,h,i,j,k;h=DWb(a.g,0).d.b;if(DWb(a.g,0).e==(AZb(),HYb)){f=_Tb(a);iTb(a,NXb);k=VUb(a,b);return new k$b(new Z1b(h,a.f),f,k)}i=DWb(a.g,0).e;if(iYb==i||(OSb(),OSb(), +!!Po(pSb,i))){g=kTb(a);if(!AVb(a,NXb)){d=g;TSb(d.a,false)&&VVb(a,YSc,CE(xE(iW,1),mLc,1,5,[g]));if(AVb(a,ZXb)){e=new c_b(new Z1b(h,a.f),d);iTb(a,ZXb);c=KTb(a,1);return new t$b(new Z1b(h,a.f),e,c)}return new r1b(new Z1b(h,a.f),g,null)}}else g=GUb(a).a;iTb(a,NXb);j=VUb(a,b);return new r1b(new Z1b(h,a.f),g,j)}function FZb(){AZb();return CE(xE(XR,1),sLc,14,0,[XXb,aYb,iYb,DXb,GXb,HXb,QXb,RXb,TXb,UXb,VXb,WXb,eYb,fYb,gYb,jYb,mYb,nYb,yYb,WYb,iZb,mZb,nZb,qZb,sZb,vZb,wZb,xZb,yZb,IXb,PXb,YXb,bYb,cYb,lYb,hZb, +kYb,oYb,sYb,JYb,RYb,SYb,TYb,fZb,zZb,FYb,KXb,GYb,LXb,HYb,MXb,MYb,ZYb,OXb,EYb,JXb,rYb,hYb,zXb,$Xb,zYb,_Xb,AYb,NYb,tYb,bZb,dZb,KYb,PYb,vYb,pYb,XYb,tZb,wXb,BXb,EXb,AXb,oZb,yXb,IYb,UYb,NXb,ZXb,OYb,uYb,cZb,eZb,LYb,qYb,YYb,uZb,xXb,CXb,FXb,$Yb,_Yb,QYb,CYb,pZb,dYb,DYb,gZb,VYb,aZb,jZb,kZb,lZb,BYb,rZb,SXb,wYb,xYb])}function Pib(a){var b,c,d,e,f,g,h,i,j,k,l,m;if(a.c.a.Yd()!=0){for(d=(h=(new Wuc(a.d.a)).a.He().Vd(),new avc(h));d.a.Id();){c=(b=d.a.Jd(),b.Ve());Sd(c.g);e9b(c.g,c)}e=new mDc;for(j=(g=(new Wuc(a.c.a)).a.He().Vd(), +new avc(g));j.a.Id();){i=(b=j.a.Jd(),b.Ve());m=i.b;f=e.a.Je(m,e);if(f==null){k=l3b((ae(Hsc(TPc,_sc(46))==-1,HMc,TPc),n8b(),new Wbc((Afc(),rec),TPc)),CE(xE(HU,1),IMc,7,0,[V3b(i.a)]));b9b(k,W7b,true);l=q3b((ae(Hsc(m,_sc(46))==-1,HMc,m),new Wbc(rec,m)),k,ufc);I9b(l,a.g);o8b(a.g,l,a.f);a.f=l}}}}function j7(b){var c,d,e,f,g,h;V6(b);h=false;if(b.G.Z.b!=(Sbb(),Obb)){for(e=new Tu(wu($t(Tt(Wt(new eyc(b.B.a),new lnb)).a),new Xt));Ru(e);){d=Su(e);for(g=y8(d).f.Vd();g.Id();){f=g.Jd();jDc(b.q,f)}}try{inb(b.B, +b.G.Z);h=true}catch(a){a=fZ(a);if(pF(a,219)){c=a;u7(b,new Gmb(null,null,-1,-1,r6,null,CE(xE(nW,1),uNc,2,6,[c.g])))}else if(pF(a,321)){c=a;u7(b,new Gmb(null,null,-1,-1,s6,null,CE(xE(nW,1),uNc,2,6,[c.g])))}else throw gZ(a);}}D6(b,b.B?new eyc(b.B.a):null);X6(b);h&&(D6(b,b.B?new eyc(b.B.a):null),Z6(b))}function NBb(a,b){var c,d,e,f,g,h,i,j,k;j=(ae(b.oi()||b.f==(dkc(),bkc),lRc,b),cgc(b.k)).Zi();if(j){k=(ae(j.oi()||j.f==(dkc(),bkc),lRc,j),cgc(j.k));if(!Esc(k.di(new stc,false).a,NMc)){a.a+=mRc;aCb(a,NNc, +k.di(new stc,true).a);a.a+=gMc}}e=new CHc;for(h=(i=(new Wuc(qjc(b).a)).a.He().Vd(),new avc(i));h.a.Id();){f=(c=h.a.Jd(),c.Ve());AHc(e,f.di(new stc,true).a)}for(g=(d=(new Yvc(e.a)).a.He().b.vj(),new ewc(d));g.a.Id();){f=(c=g.a.Jd(),c.Ve());a.a+=mRc;ptc(ptc(ptc(ptc((a.a+="@",a),ONc)," {"),f),"}");a.a+=gMc}}function iQb(){iQb=JZ;var a;TPb=new uQb((a=erc(FR),new TCc(a,nKc(a,a.length),0)));YPb=qQb(TPb,MRb(0));ZPb=qQb(YPb,MRb(1));_Pb=qQb(ZPb,MRb(2));$Pb=sQb(_Pb,(IRb(),iRb),CE(xE(FR,1),sLc,24,0,[]));bQb= +qQb(_Pb,MRb(3));aQb=sQb(bQb,iRb,CE(xE(FR,1),sLc,24,0,[]));dQb=qQb(bQb,MRb(4));cQb=sQb(dQb,iRb,CE(xE(FR,1),sLc,24,0,[]));VPb=qQb(dQb,MRb(5));UPb=sQb(VPb,iRb,CE(xE(FR,1),sLc,24,0,[]));XPb=qQb(VPb,MRb(6));WPb=sQb(XPb,iRb,CE(xE(FR,1),sLc,24,0,[]));eQb=qQb(XPb,MRb(7));fQb=qQb(eQb,MRb(8));gQb=qQb(eQb,MRb(9));hQb=WPb}function yUb(a){var b,c,d,e,f,g,h,i,j,k;j=DWb(a.g,0).d.b;iTb(a,(AZb(),lYb));if(AVb(a,gZb)){f=iTb(a,gZb);nTb(a);return new e_b(new Z1b(j,a.f),null,null,null,f)}b=null;h=null;c=null;i=true;k= +DWb(a.g,0).e;if(iYb==k||a.b.d&&VSb(k)||!a.b.c&&USb(k)){b=jTb(a);AVb(a,OXb)?iTb(a,OXb):i=false}else if(SSb(DWb(a.g,0).e)){d=wTb(a);SVb(a,d,YSc,CE(xE(iW,1),mLc,1,5,[d]))}if(i)if(AVb(a,bZb)){iTb(a,bZb);oTb(a,"as");h=jTb(a)}else c=AUb(a);oTb(a,KNc);g=iTb(a,gZb);e=!g?null:g;nTb(a);return new e_b(new Z1b(j,a.f),b,c,h,e)}function s2b(a){var b,c,d,e;d=0;c=new stc;b=a.b.b;if(pF(a.a,156))e=a.a.a;else if(pF(a.a,132))e=a.a.a;else throw gZ(g2b(a));if(a.b.i)if(b==111){c.a+="0";d+=1}else{c.a+="0x";d+=2}if(100== +b){a.b.e?otc(c,a.a):(c.a+=zZ(e),c);if(iZ(e,0)<0)if(a.b.g)return v2b(a,c);else a.b.k&&++d;else if(a.b.d){rtc(c,0,String.fromCharCode(43));d+=1}else if(a.b.j){rtc(c,0,String.fromCharCode(32));d+=1}}else{pF(a.a,132)&&(e=jZ(TE(pZ(e)?wZ(e):e,pZ(kTc)?wZ(kTc):kTc)));111==b?ptc(c,gsc(e,3)):ptc(c,gsc(e,4))}return j2b(a,c,d)}function mNb(a,b){var c,d,e;e=a.j.g;d=lOb(a.j);if(b==(fOb(),$Nb)){b=!a.o?mOb(a.j):GMb(a);if(b==PNb||b==UNb||b==cOb||b==aOb||b==bOb||b==ZNb||b==_Nb||b==RNb||b==TNb||b==SNb){a.o=b;return QMb(a, +(Afc(),Pec))}return ANb(a,(Afc(),Pec),WMb(a,b),e,d)}else if(b==NNb)return ANb(a,(Afc(),Qcc),WMb(a,!a.o?mOb(a.j):GMb(a)),e,d);else{c=WMb(a,b);e=a.j.g;d=lOb(a.j);if(c)if(OMb(a,$Nb)){!a.o?mOb(a.j):GMb(a);return ANb(a,(Afc(),Pec),c,e,d)}else if(OMb(a,NNb)){!a.o?mOb(a.j):GMb(a);return ANb(a,(Afc(),Qcc),c,e,d)}return c}}function Bdb(a,b){var c;switch(b){case kPc:case "ArrayBuffer":case lPc:case "DataView":case "Date":case mPc:case nPc:case "Generator":case "GeneratorFunction":case "Int16Array":case "Int32Array":case "Int8Array":case "InternalError":case "Map":case oPc:case pPc:case "Proxy":case qPc:case "Set":case oMc:case rPc:case "TypedArray":case "Uint16Array":case "Uint32Array":case "Uint8Array":case "Uint8ClampedArray":case "WeakMap":case "WeakSet":return c= +i$(Kob(a),b),!(!!c&&!(!c.a||B8(c.a)));default:return false}}function cgb(a,b){var c,d,e,f,g,h,i,j,k;(b.a.a&IPc)==0&&(b.a.a&JPc)==0&&(b.a.a&vMc)==0&&m7b(b);if(a){(f=!a.d?null:a.d.q,!f?(Cyc(),Cyc(),Byc):f).Ud()||n7b(b,(e=!a.d?null:a.d.q,!e?(Cyc(),Cyc(),Byc):e));for(d=G4b(a).Vd();d.Id();){c=d.Jd();f7b(b,c,H4b(a,c));g7b(b,c,z4b(a,c))}for(j=K4b(a).Vd();j.Id();){i=j.Jd();r7b(b,i);q7b(b,i,L4b(a,i))}k=a.j;!!k&&k!=(e6b(),_5b)&&y7b(b,k);(a.a&256)!=0&&A6b(b);(!a.d?null:a.d.c)!=null&&x4b(b.a)==null&&B6b(b,!a.d? +null:a.d.c);e5b(b.a,a);for(h=J4b(a).Vd();h.Id();){g=h.Jd();o7b(b,g)}}}function dlc(a){hlc(a,a.u[0]);hlc(a,a.u[3]);hlc(a,a.u[5]);hlc(a,a.u[7]);hlc(a,a.u[9]);hlc(a,a.u[8]);hlc(a,a.u[24]);hlc(a,a.u[26]);hlc(a,a.u[20]);hlc(a,a.u[12]);hlc(a,a.u[29]);hlc(a,a.u[22]);hlc(a,a.u[27]);hlc(a,a.u[31]);hlc(a,a.u[32]);flc(a,a.u[32],"Null");hlc(a,a.u[34]);hlc(a,a.u[33]);hlc(a,a.u[38]);hlc(a,a.u[36]);hlc(a,a.u[41]);hlc(a,a.u[43]);hlc(a,a.u[45]);hlc(a,a.u[46]);hlc(a,a.u[48]);flc(a,a.u[49],ETc);hlc(a,a.u[52]);flc(a, +a.u[52],"Undefined");flc(a,a.u[52],XNc);flc(a,a.u[17],nPc);flc(a,a.u[61],"Global")}function Ypb(a){var b,c,d,e,f,g;switch(a.n.f){case 31:case 91:return a.Wh();case 118:g="";for(c=a.c;c;c=c.f){d=c;c.n==(Afc(),kfc)&&(d=c.c);e=Ypb(d);if(e==null)return null;g=g+e}return g;case 120:return a.Th();case 29:f=a.Wh();if(Esc(vPc,f)||Esc(xQc,f)||Esc("NaN",f))return f;break;case 30:return""+a.Uh();case 34:return SNc;case 35:return TNc;case 32:return jLc;case 79:return vPc;case 19:b=Spb(a.c);if(b!=(yoc(),xoc))return b.gj(true)? +SNc:TNc;break;case 42:return spb(a);case 43:return"[object Object]"}return null}function mkb(a,b,c){var d,e,f,g,h,i,j,k;j=false;k=(ppb(),upb(c,false,a.b));d=c;for(i=new iac(new eac(Sd(d.g)));i.a.a;){h=hac(i);if(!!h&&(h.n==(Afc(),ufc)||h.n==jec||h.n==gdc)&&!(h==d.g&&!(d==d.g.c?null:d.i)))return 0;if(h==b)return j?2:1;if(ukb(h))d!=h.c&&(j=true);else if(lkb(a,h,k));else{e=tkb(h);for(g=e==0?h.c:h.c?h.c.i:null;g;g=e==0?g.f:g==g.g.c?null:g.i){if(g==d)break;if(kkb(a,g,k)){k=true;j=true}}f=h.c;if(j&&h.n== +(Afc(),Ycc)&&(f.n==(Afc(),Pdc)||f.n==Odc))return a.a?2:0}d=h}throw gZ(new Irc("Unexpected."));}function C9b(a,b,c,d,e){var f,g,h,i,j,k;otc(b,a.n);if(pF(a,9)){b.a+=" ";ptc(b,a.Wh())}else if(a.n==(Afc(),Jdc)){b.a+=" ";!a.c||a.c.n!=rec?(b.a+="",b):ptc(b,a.c.Wh())}else if(a.n==Cec){b.a+=" ";jtc(b,a.Uh())}if(c){h=T9b(a.k);if(h!=-1){b.a+=" ";b.a+=h}if(a.e!=0){b.a+=" [length: ";ktc(b,a.e);b.a+="]"}}if(d){g=K8b(a);for(f=0;f",gNc),SFb(d))),"^\\./",""),"/","$"),92,36),64,36),43,36),45,95),58,95),46,95),cNc,"_")+"$classextends$var"+a.a++);h=(ppb(),Gpb(c,new jrb));g=c.c.f;k9b(g,F9b((ae(Hsc(f,_sc(46))==-1,HMc,f),n8b(), +new Wbc((Afc(),rec),f)),g));e=I9b(q3b((ae(Hsc(f,_sc(46))==-1,HMc,f),new Wbc(rec,f)),g,gdc),g);p8b(h.g,e,h);qpb(Gpb(c,new frb(Xec)),(IRb(),RQb));w7(b.c,c)}function bNb(a,b,c){var d,e;while(true)switch(b.f){case 13:if(a.i==(JNb(),HNb)){a.i=INb;b=VMb(a,b,c)}else b=!a.o?mOb(a.j):GMb(a);break;case 16:d=true;EMb(a,c);if(m6b(a.f)){a.e=k6b(a.f);e=a.e.j;switch(e.f){case 0:case 2:AMb(a,Zb(e.e!=null?e.e:""+e.f));d=false}}return d;case 0:k6b(a.f);zMb(a,"msg.unexpected.eof",a.j.g,lOb(a.j));EMb(a,c);return false; +case 1:a.i==(JNb(),INb)&&(a.i=HNb);b=!a.o?mOb(a.j):GMb(a);break;default:if(b==(fOb(),dOb)&&a.i==(JNb(),HNb))b=!a.o?mOb(a.j):GMb(a);else{a.i=(JNb(),INb);b=HMb(a,!a.o?mOb(a.j):GMb(a))}}}function N4b(a){var b,c,d,e,f,g,h,i,j;e=new fxc;!!a.i&&Ywc(e,a.i.a);!!a.g&&Ywc(e,a.g.a);if(a.d){!!a.d.a&&Ywc(e,a.d.a.a);if(a.d.f)for(d=new wxc(a.d.f);d.a1&&u7(b,new Gmb(null,null,-1,-1,q6,null,CE(xE(nW,1),uNc,2,6,[d.a[0].e])));b.k=f7(c);try{b.B=new jnb(d)}catch(a){a=fZ(a);if(pF(a,319)){f=a;u7(b,new Gmb(null,null,-1,-1,t6,null,CE(xE(nW,1),uNc,2,6,[f.b.e,f.a.e])));return}else throw gZ(a);}D6(b,b.B?new eyc(b.B.a):null);bnb(b.B);new SHc;xg(b.t,b.G.Lb);Z6(b);b.v=T3b(CE(xE(HU,1),IMc,7,0,[]));b.o=T3b(CE(xE(HU,1),IMc,7,0,[]));b.i=T3b(CE(xE(HU, +1),IMc,7,0,[b.o,b.v]))}function phb(a,b,c,d){var e,f,g,h,i,j,k;j=jhb(a,g9b(b));f=b.c;h=f.f;e=h.f;if(E8b(f,(Hbc(),gbc))==0&&E8b(h,gbc)==0&&E8b(e,gbc)==0){r8b(b,j);c9b(b,gbc,0);whb(a,b);return}if(j.n!=(Afc(),tdc)){H3b(j)&&(j=F9b((Yd(H3b(j),j),new L9b(Ddc,j)),j));whb(a,j)}k=new pib(a.a);i=Thb(a.a,d);g=Thb(a.a,c);cib(a.a,k);if(f.n!=tdc){f=khb(a,jhb(a,z8b(f)));eib(a.a,F9b(B3b(F9b(Yjb(Z3b(zec,f),a.f.a),f),Ghb(a.a,g,true,b)),b))}Yhb(a.a,g,i);thb(a,z8b(e),null,null);Vhb(a.a);cib(a.a,i);if(h.n!=tdc){h=jhb(a, +z8b(h));whb(a,F9b((Yd(H3b(h),h),new L9b(Ddc,h)),h))}gib(a.a,k,b);cib(a.a,g)}function Dqb(a){ppb();var b,c,d,e,f,g,h,i;f=(g=Dpb(a),!g?null:H8b(g,(Hbc(),mbc)));if(!!f&&N4b(f).a.length!=0)return false;e=a.g.n==(Afc(),gdc)||!!f&&((f.a&37)!=0||(!f.d?null:f.d.d)!=null);c=H8b(a,(Hbc(),ybc));if(!(!!c&&c.Kh())&&!e)return false;if(Cqb(a.g)){i=a;d=a.c}else if(a.n==Ddc){b=a.c;if(b.n!=Ccc||b.c.n!=Pdc)return false;i=b.c;d=b.c?b.c.i:null}else if(a.n==Pdc){h=a.g;if(h.n!=Ccc||h.g.n!=Ddc)return false;i=a;d=h.c?h.c.i: +null}else return false;if(!d||!i)return false;if(d.n==Eec)return true;return d.n==Hec&&$8b(i,d.c)&&(d.c?d.c.i:null).n==Eec}function Veb(a,b,c){var d,e,f,g,h;g=c.f;if(c.e){f=r9b((ae(Hsc(EPc,_sc(46))==-1,HMc,EPc),n8b(),new Wbc((Afc(),rec),EPc)),c.g);h=q3b(f,r9b(new J9b(lfc),c.g),gdc);qpb(b.f,(IRb(),RQb));I9b(h,g);Xeb(a,h);!c.c?r8b(g,h):o8b(g,h,c.c);w7(a.a,h)}if(c.d){f=r9b((ae(Hsc(FPc,_sc(46))==-1,HMc,FPc),n8b(),new Wbc((Afc(),rec),FPc)),c.a);d=q3b(f,r9b((ae(Hsc(BMc,_sc(46))==-1,HMc,BMc),new Wbc(rec, +BMc)),c.a),gdc);qpb(b.f,(IRb(),RQb));r8b(g,d);e=new C7b(false);s7b(e,new H7b(new L9b(Qcc,new Wbc(bfc,"Arguments")),""));q9b(d,k6b(e));I9b(d,g);w7(a.a,d)}}function WKb(a,b){var c,d,e,f,g,h;f=false;c=true;for(d=0;d>13|(a.m&15)<<9;e=a.m>>4&8191;f=a.m>>17|(a.h&255)<<5;g=(a.h&1048320)>>8;h=b.l&8191;i=b.l>>13|(b.m&15)<<9;j=b.m>>4&8191;k=b.m>>17|(b.h&255)<<5;l=(b.h&1048320)>>8;B=c*h;C=d*h;D=e*h;F=f*h; +G=g*h;if(i!=0){C+=c*i;D+=d*i;F+=e*i;G+=f*i}if(j!=0){D+=c*j;F+=d*j;G+=e*j}if(k!=0){F+=c*k;G+=d*k}l!=0&&(G+=c*l);n=B&sMc;o=(C&511)<<13;m=n+o;q=B>>22;r=C>>9;s=(D&262143)<<4;t=(F&31)<<17;p=q+r+s+t;v=D>>18;w=F>>5;A=(G&xMc)<<8;u=v+w+A;p+=m>>22;m&=sMc;u+=p>>22;p&=sMc;u&=tMc;return HE(m,p,u)}function MTb(a,b){var c,d,e,f,g,h,i;i=DWb(a.g,0).d.b;oTb(a,GNc);g=DWb(a.g,0).d.b.b>a.f.b;(g||zVb(a,0,(AZb(),ZYb))||zVb(a,0,(AZb(),KXb))||zVb(a,0,(AZb(),XXb)))&&VVb(a,"No newline allowed between `async` and arrow function parameter list", +CE(xE(iW,1),mLc,1,5,[]));d=null;if(AVb(a,(AZb(),GYb)))d=pUb(a,0);else{h=wUb(a);d=new G$b(h.o,(Bn(),new Wz(Sd(h))))}f=DWb(a.g,0).d.b.b>a.f.b;(f||zVb(a,0,ZYb)||zVb(a,0,KXb)||zVb(a,0,XXb))&&VVb(a,VSc,CE(xE(iW,1),mLc,1,5,[]));iTb(a,zXb);c=JTb(a,b,(pWb(),lWb));e=M$b(L$b(K$b(new T$b((Y$b(),U$b))),d),c);return I$b(e,new Z1b(i,a.f))}function rLb(a,b){var c,d,e,f,g,h,i,j,k;bKb(a.a,b,(IRb(),MQb));j=pMb(a,b.d,b);aLb(a,j,b.b);k=qMb(a,b.e,b);k.n==(Afc(),tdc)||(a.a.e=oQb(a.a.e,NQb));i=oMb(a,Udc,b.c);c=dKb(a.a, +cdc);T9b(c.k)==-1&&vKb(c,b.o.b,b.o.a);h=false;for(e=b.a.Vd();e.Id();){d=e.Jd();switch(d.p.f){case 88:case 89:gLb(a,d,hRb)}switch(d.p.f){case 37:case 39:case 33:case 34:a.a.e=oQb(a.a.e,OQb)}f=rMb(a,d);if(f){h&&a.a.d.Ah("Class may have only one constructor.",a.a.q,(RJb(),d.o.b.b+1),d.o.b.a);h=true}q8b(c,yKb(a.a,d))}g=gKb(a.a,bdc,j,k,c);if(i.n!=tdc){gLb(a,b,bRb);d9b(g,(n8b(),$7b),i)}return g}function Orb(a,b){var c,d,e,f,g,h,i,j,k;if(E8b(b,(n8b(),U7b))!=0)return;c=b.c;ppb();if(!!c&&(c.n==(Afc(),ufc)|| +c.n==jec||c.n==gdc)||c.n==(Afc(),Jdc)&&iqb(c.g)&&(c.n==Jdc&&Gqb(c.c))||c.n==(Afc(),bdc)&&iqb(c.g)&&(c.n==bdc&&Gqb(c.c))){e9b(b,c);g=F9b(new J9b((Afc(),Cdc)),b);r8b(b,g);if(c.n==bdc||c.n==Jdc){j=Jyc(c.c);p8b(b.g,c,b)}else{j=Bpb(c);for(e=(!c.c?(Cyc(),Cyc(),Byc):new Kbc(c.c)).Vd();e.Id();){d=e.Jd();e9b(c,d);k=F9b(new L9b(c.n,d),b);p8b(b.g,k,b)}}for(i=j.Vd();i.Id();){h=i.Jd();f=F9b(new J9b(Bdc),h);r8b(f,h.Sh(false));r8b(f,h.Sh(false));q8b(g,f)}w7(a.b,b.g)}}function e7(a,b,c,d){var e,f,g,h,i,j,k,l,m,n; +if(a.G.yc){b.c.a.length>0&&!(b.c.a.length>gMc.length&&Esc(gMc,xqc(b.c,b.c.a.length-gMc.length)))&&$7(b,gMc);Xd(c.n==(Afc(),Xec));f=a.G.Jb;i=H8b(c,(Hbc(),cbc)).a;n=(g=H8b(c,ybc),!g?null:g.ug());Xd(n!=null);Xd(n.length!=0);f=Osc(Osc(Osc(f,"%name%",ZIc(i)),"%num%",""+d),"%n%",gMc);$7($7(b,f),gMc)}if(H8b(c,(Hbc(),mbc))){l=D4b(H8b(c,mbc));l!=null&&jDc(b.d,l)&&$7($7($7(b,"/*\n"),l),"*/\n")}e=F7(a,c,d==0);if(e.length!=0){$7(b,e);k=e.length;j=e.charCodeAt(k-1);m=k>=2?e.charCodeAt(k-2):0;h=j==59||j==10&&m== +59;h||$7(b,";")}return null}function EHb(){EHb=JZ;DHb=new Mcb("JSC_REQUIRES_NOT_SORTED",(E2(),D2),new Rtc("goog.require() and goog.requireType() statements are not sorted. (Fix with go/fixjs) The correct order is:\n\n{0}\n"));CHb=new Mcb("JSC_PROVIDES_NOT_SORTED",D2,new Rtc("goog.provide() statements are not sorted. (Fix with go/fixjs) The correct order is:\n\n{0}\n"));BHb=new Mcb("JSC_PROVIDES_AFTER_REQUIRES",D2,new Rtc("goog.provide() statements should be before goog.require() and goog.requireType() statements.")); +AHb=new Mcb("JSC_DUPLICATE_REQUIRE",D2,new Rtc("''{0}'' required more than once."))}function XTb(a,b){var c,d;if(AVb(a,(AZb(),ZYb)))return cUb(a);else{d=new sWb(DWb(a.g,0).d.b);d.b=b;d.a=a.b.d&&QCc(MCc(TYb,CE(xE(XR,1),sLc,14,0,[SYb,RYb])),DWb(a.g,0).e)?wTb(a).e:null;d.c=!!mTb(a,fZb);return zVb(a,0,iYb)&&Esc(DWb(a.g,0).a,nNc)&&(KVb(a,1)||DWb(a.g,1).e==HYb)?uUb(a,d):zVb(a,0,iYb)&&Esc(DWb(a.g,0).a,"set")&&(KVb(a,1)||DWb(a.g,1).e==HYb)?eVb(a,d):zVb(a,0,iYb)&&Esc(DWb(a.g,0).a,GNc)&&(c=DWb(a.g,1).d.b.b> +DWb(a.g,0).d.a.b,!(c||zVb(a,1,ZYb)||zVb(a,1,KXb)||zVb(a,1,XXb)))&&(KVb(a,1)||DWb(a.g,1).e==HYb||zVb(a,1,bZb)&&(KVb(a,2)||DWb(a.g,2).e==HYb))?OTb(a,d):ZTb(a,d)}}function qD(b){var c,d,e,f,g,h,i,j,k,l,m,n,o,p,q;c=new oD;f=null;try{f=$wnd.JSON.parse(b)}catch(a){a=fZ(a);if(pF(a,38)){d=a;throw gZ(new rD("JSON parse exception: "+d));}else throw gZ(a);}q=f;nD(c,q.version);iD(c,q.file);c.c=-1;jD(c,q.mappings);if(q.sections!=null){l=(Bn(),new Zr);for(n=q.sections,o=0,p=n.length;o>19!=0){b=XE(b);i=!i}g=PE(b);f=false;e=false;d=false;if(a.h==uMc&&a.m==0&&a.l==0){e=true;f=true;if(g==-1){a=GE((iF(),eF));d=true;i=!i}else{h=ZE(a,g);i&&NE(h);c&&(EE=HE(0,0,0));return h}}else if(a.h>>19!=0){f=true;a=XE(a);d=true;i=!i}if(g!=-1)return KE(a, +g,i,f,c);if(UE(a,b)<0){c&&(f?EE=XE(a):EE=HE(a.l,a.m,a.h));return HE(0,0,0)}return LE(d?a:HE(a.l,a.m,a.h),b,i,f,e,c)}function rE(a,b){var c,d,e,f,g,h,i,j,k;j="";if(b.length==0)return a.hg(rMc,pMc,-1,-1);k=Ysc(b);Esc(k.substr(0,3),"at ")&&(k=k.substr(3));k=k.replace(/\[.*?\]/g,"");g=k.indexOf("(");if(g==-1){g=k.indexOf("@");if(g==-1){j=k;k=""}else{j=Ysc(k.substr(g+1));k=Ysc(k.substr(0,g))}}else{c=k.indexOf(")",g);j=k.substr(g+1,c-(g+1));k=Ysc(k.substr(0,g))}g=Hsc(k,_sc(46));g!=-1&&(k=k.substr(g+1)); +(k.length==0||Esc(k,"Anonymous function"))&&(k=pMc);h=Jsc(j,_sc(58));e=Ksc(j,_sc(58),h-1);i=-1;d=-1;f=rMc;if(h!=-1&&e!=-1){f=j.substr(0,e);i=mE(j.substr(e+1,h-(e+1)));d=mE(j.substr(h+1))}return a.hg(f,k,i,d)}function K1(a,b){var c,d,e,f;if(!a||!b)return!a&&!b;if(!a.Xh(b,false,false,false,true))return false;if(C8b(a)!=C8b(b))return false;if(a.n==(Afc(),Jdc)&&b.n==Jdc){ppb();if((a.n==Jdc&&iqb(a.g)&&a.n==Jdc&&Gqb(a.c))!=(b.n==Jdc&&iqb(b.g)&&b.n==Jdc&&Gqb(b.c)))return false}if(!!a.g&&a.g.n==Jec)if(E8b(a, +(Hbc(),lbc))!=0!=(E8b(b,lbc)!=0))return false;e=a.c;c=b.c;while(!!e&&!!c){if(e.n==Jdc||e.n==Xec){if(c.n!=e.n)return false;if(e.n==Jdc&&(ppb(),e.n==Jdc&&iqb(e.g)&&e.n==Jdc&&Gqb(e.c))){f=e.c.Wh();d=c.c.Wh();if(!Esc(f,d))return false}}else if(!K1(e,c))return false;e=e.f;c=c.f}return true}function Spb(a){var b;switch(a.n.f){case 118:if(!!a.c&&!a.c.f)return Spb(a.c);break;case 120:return yoc(),null!=a.Th()&&a.Th().length!=0?woc:voc;case 31:return yoc(),a.Wh().length>0?woc:voc;case 30:return yoc(),a.Uh()!= +0?woc:voc;case 19:return Spb(a.c?a.c.i:null).fj();case 32:case 34:return yoc(),voc;case 79:if(!upb(a.c,false,null))return yoc(),voc;break;case 29:b=a.Wh();if(Esc(vPc,b)||Esc("NaN",b))return yoc(),voc;else if(Esc(xQc,b))return yoc(),woc;break;case 35:case 38:return yoc(),woc;case 65:case 96:case 23:case 42:case 43:if(!upb(a,false,null))return yoc(),woc}return yoc(),xoc}function Zfb(a,b,c){var d,e,f,g,h,i,j,k,l,m,n,o,p,q;h=E8b(c,(Hbc(),zbc))!=0?b.e:b.f;i=c.n==(Afc(),fdc)?Kpb(h,c.c):Lpb(h,c.Wh());if(!i){i= +r9b(P3b(CE(xE(HU,1),IMc,7,0,[(k=W3b("configurable",(l=new J9b(nfc),l)),k),(m=W3b(GPc,(n=new J9b(nfc),n)),m)])),a.b);if(c.n==fdc)q8b(h,(o=p3b(x8b(c.c,false),i),o));else{q=(o=W3b(c.Wh(),i),o);c.Yh()&&b9b(q,(n8b(),i8b),true);q8b(h,q)}}e=c.c?c.c.i:null;f=E7b((ppb(),g=Dpb(e),!g?null:H8b(g,mbc)));p7b(f,new H7b(new L9b(Qcc,V3b(I8b(b.g))),(d=H8b(c,ybc),!d?null:d.ug())));p=(j=W3b(c.n==Qdc||E8b(c,(n8b(),M7b))!=0?nNc:"set",z8b(e)),j);q9b(p,k6b(f));q8b(i,p);I9b(i,c)}function rlb(a,b,c,d,e,f){var g,h,i;if(ylb(a, +b,c,d,e,f))return;switch(e.n.f){case 84:case 66:case 41:case 25:case 79:case 19:case 20:case 21:case 22:i=(cmb(),Zlb);break;case 28:d==e.c?i=(cmb(),Xlb):vlb(a,e)?i=(cmb(),Zlb):i=(cmb(),Wlb);break;case 23:i=d==e.c?(cmb(),Zlb):(cmb(),Wlb);break;case 61:case 62:i=mlb(e,f);break;case 60:d!=e.c?i=mlb(e,f):i=(cmb(),Zlb);break;case 24:i=(cmb(),Ylb);break;case 96:i=(cmb(),bmb);break;case 94:i=(cmb(),Wlb);break;case 95:case 47:g=d==d.g.c?null:d.i;i=g.n==(Afc(),Fec)?(cmb(),Zlb):(cmb(),Wlb);break;default:i= +(cmb(),Wlb)}h=plb(a,f);jlb(a,h,d,i,b,c)}function rTb(a,b){var c,d,e,f,g,h,i;if(b.p==(n1b(),q0b)){f=b.a;return Esc(f.a,GNc)&&(h=DWb(a.g,0).d.b.b>a.f.b,!(h||zVb(a,0,(AZb(),ZYb))||zVb(a,0,(AZb(),KXb))||zVb(a,0,(AZb(),XXb))))&&(i=DWb(a.g,0).e,(AZb(),iYb)==i||a.b.d&&VSb(i)||!a.b.c&&USb(i))&&(g=DWb(a.g,1).d.b.b>DWb(a.g,0).d.a.b,!(g||zVb(a,1,(AZb(),ZYb))||zVb(a,1,(AZb(),KXb))||zVb(a,1,(AZb(),XXb))))&&zVb(a,1,(AZb(),zXb))}else if(b.p==I_b){d=b;e=d.b;c=d.a;return e.p==q0b&&Esc(e.a.a,GNc)&&e.o.a.b==c.o.b.b&& +(g=DWb(a.g,0).d.b.b>a.f.b,!(g||zVb(a,0,(AZb(),ZYb))||zVb(a,0,(AZb(),KXb))||zVb(a,0,(AZb(),XXb))))&&AVb(a,(AZb(),zXb))}else return false}function UEb(a){var b,c,d,e,f,g,h,i,j,k,l,m;for(l=new wxc(a.f);l.a",gNc),SFb(b)))),j);for(f=j.mh().Vd();f.Id();){e=f.Jd(); +_tc(a.b,e,j)}}for(m=new wxc(a.f);m.aa.f.b,(h||zVb(a,0,ZYb)||zVb(a,0,KXb)||zVb(a,0,XXb))&&VVb(a,VSc,CE(xE(iW,1),mLc,1,5,[])),iTb(a,zXb),i=JTb(a,b,(pWb(),oWb)),j=M$b(L$b(new T$b((Y$b(),U$b)),g),i),I$b(j, +pTb(a,g.o.b)));if(CVb(a)){c=WVb(a,c);if(!HZb(c)){VVb(a,WSc,CE(xE(iW,1),mLc,1,5,[]));return new n_b(pTb(a,DWb(a.g,0).d.b))}d=wTb(a);e=KTb(a,b);return new RZb(new Z1b(f,a.f),c,d,e)}return c}function s_(a,b,c){var d,e,f,g,h,i,j,k,l,m,n;switch(b.n.f){case 98:F_(a,(IRb(),gRb),b);g0(b);j=Cfc(b.n);j!=-1&&(e=C8b(b),j!=e&&D0(QMc+j+RMc+e,b));n=b.c;c?G_(a,n,true):G_(a,n,false);break;case 86:case 87:F_(a,(IRb(),OQb),b);g0(b);k=Cfc(b.n);k!=-1&&(f=C8b(b),k!=f&&D0(QMc+k+RMc+f,b));m=b.c;c?G_(a,m,true):G_(a,m,false); +break;case 159:g=C8b(b);0!=g&&D0(OMc+g,b);break;case 116:w_(a,b);break;case 160:l=Cfc(b.n);l!=-1&&(h=C8b(b),l!=h&&D0(QMc+l+RMc+h,b));q_(b.c,0);break;case 161:i=Cfc(b.n);i!=-1&&(d=C8b(b),i!=d&&D0(QMc+i+RMc+d,b));break;case 80:break;default:D0("Class contained member of invalid type "+b.n,b)}}function q2b(a){var b,c,d,e,f;if(a.a==null)return a.b.k=false,j2b(a,jLc,0);if(!rF(a.a))throw gZ(g2b(a));e=t2b(a);if(e!=null)return e;b=a.b.b;b!=97&&b!=65&&a.b.n==-1&&(a.b.n=6,undefined);d=new stc;switch(b){case 97:case 65:l2b(a); +break;case 101:case 69:m2b(a,d);break;case 102:n2b(a,d);break;case 103:case 71:u2b(a,d);break;default:throw gZ(O2b(a.b));}a.b.n=-1;f=0;if(45==d.a.charCodeAt(0)){if(a.b.g)return v2b(a,d)}else{if(a.b.j){rtc(d,0,String.fromCharCode(32));++f}if(a.b.d){rtc(d,0,String.fromCharCode(43));++f}}c=d.a.charCodeAt(0);a.b.k&&(c==43||c==45)&&(f=1);(b==97||b==65)&&(f+=2);return j2b(a,d,f)}function rhb(a,b,c){var d,e,f,g,h,i,j,k,l,m,n;e=jhb(a,g9b(b));i=b.c;f=i.f;if(E8b(i,(Hbc(),gbc))==0&&(!f||E8b(f,gbc)==0)){r8b(b, +e);c9b(b,gbc,0);whb(a,b);return}z8b(i);!f?f=F9b((d=new J9b((Afc(),Vcc)),d),b):z8b(f);if(E8b(i,gbc)!=0&&E8b(f,gbc)==0){e=F9b(Yjb(Z3b((Afc(),zec),e),a.f.a),e);n=i;i=f;f=n}if(E8b(i,gbc)==0){h=Thb(a.a,c);k=Ghb(a.a,h,false,i);while(k.c){l=g9b(k);c9b(l,hbc,1);q8b(i,l)}whb(a,F9b(B3b(e,i),b));thb(a,f,null,null);cib(a.a,h);return}j=new pib(a.a);g=Thb(a.a,c);e=khb(a,e);m=Ghb(a.a,j,true,b);eib(a.a,F9b(B3b(khb(a,e),m),b));thb(a,f,null,null);gib(a.a,g,f);cib(a.a,j);thb(a,i,null,null);cib(a.a,g)}function E6(a, +b){var c,d,e,f,g,h,i,j,k,l,m,n;V6(a);e=new fxc;i=new fDc;h=new fDc;for(g=new Tu(wu($t(Tt(Wt(new eyc(a.B.a),new lnb)).a),new Xt));Ru(g);){f=Su(g);a.G.Z.b!=(Sbb(),Pbb)&&y8(f).f.Ud()&&(e.a[e.a.length]=f,true);_tc(h,n1(o1(z8(f).a)),f);for(m=y8(f).f.Vd();m.Id();){l=m.Jd();Esc(l.substr(0,7),hOc)||(l==null?FDc(i.d,null,f):ZDc(i.e,l,f))}}for(k=xu(new Wzc(a.G.Z.a.a.b.Vd()));k.Id();){j=k.Jd();f=Ytc(i,n1(j));!f&&(f=Ytc(h,n1(j)));!!f&&(e.a[e.a.length]=f,true)}n=sz(new Tu(wu($t(Tt(Wt(new eyc(a.B.a),new lnb)).a), +new Xt)));for(d=new wxc(e);d.a=2&&(a.charCodeAt(c-1)==45&&a.charCodeAt(c-2)==45||a.charCodeAt(c-1)==93&&a.charCodeAt(c-2)==93)?(d.a+="\\u003e",d):(d.a+=String.fromCharCode(b),d);break;case 60:Msc(a,true,c+1,"/script",0,7)?(d.a+="\\u003c",d):Msc(a,false,c+1,"!--",0,3)?(d.a+="\\u003c",d):(d.a+=String.fromCharCode(b),d);break;default:b>31&&b<=127?(d.a+=String.fromCharCode(b),d):wD(d,b)}}d.a+='"';return d.a}function Gjb(a){var b,c,d,e,f;f=a.d.d;a.b=Mpb(f);switch(f.n.f){case 65:{e= +f.c;b=e.f;Xd(b.n==(Afc(),Jec));Ejb(a,a.d,b);d=e.Wh();d.length!=0&&(ppb(),f.n==Jdc&&!(f.n==Jdc&&iqb(f.g)&&f.n==Jdc&&Gqb(f.c))&&!Bqb(f))&&Fjb(a,a.d,e);return}case 96:{c=f.c;c.n!=(Afc(),tdc)&&(ppb(),f.n==bdc&&(!(f.n==bdc&&Gqb(f.c))||!iqb(f.g)))&&Fjb(a,a.d,c);return}case 81:case 85:Yd(!a.d.b,a.d);Hjb(a,f,a.d,a.d);return;case 112:Hjb(a,f,a.d,a.d);return;case 72:case 101:case 102:case 73:case 67:Hjb(a,f,null,a.d);return;case 82:ppb();f.n==(Afc(),Vcc)&&!!f.g&&f.g.n==Jdc?Hjb(a,f,a.d,a.d):Hjb(a,f,null,a.d); +return;default:throw gZ(new GD("Illegal scope root: "+f));}}function BKc(a,b){var c,d,e,f,g,h,i,j,k;e=0;for(i=0;ib)throw gZ(new Fqc(QTc));}f=zE(xF,eLc,46,e,15,1);k=0;g=0;for(h=0;h0){c=a[h++];if((c&192)!=128)throw gZ(new qnb("Invalid UTF8 sequence at "+(h-1)+", byte="+(j=c>>>0,j.toString(16))));d=d<<6|c&63}k+=Zqc(d,f,k)}return f}function TBb(a,b){var c,d,e,f,g,h;h=b.d;Xd(b.n==(Afc(),Jdc)||!!h.Ii());if(!h||h.Bi())return"";c=h.Ii();if(Rfc(h,a.b.u[17]))return"/** @type {!Function} */\n";f=new vtc("/**\n");d=null;!!b&&b.n==Jdc&&(d=(ppb(),Kd(b.n==Jdc),b.c.f).c);OBb(a,f,c,d);e=c.b.b;if(!!e&&!(e.ti()||e.ri()||e.si()||e==e.A.u[64])&&c.f!= +(dkc(),bkc)&&!(c.oi()&&e.Ci())){f.a+=mRc;aCb(f,ENc,Pfc(e,new stc,true).a);f.a+=gMc}if(c.oi()){NBb(f,c);f.a+=" * @constructor\n"}else if(c.f==(dkc(),bkc))PBb(f,c);else{g=Yfc(c.k)?c.A.u[51]:c.k;if(!!g&&!g.Bi()&&!g.Ci())if(!b||!Rfc(g,RBb(a,b))){f.a+=mRc;aCb(f,FMc,Pfc(g,new stc,true).a);f.a+=gMc}}QBb(f,Vfc(c));f.a+=" */\n";return f.a}function H3b(a){switch(a.n.f){case 65:case 96:return true;case 13:case 62:case 42:case 47:case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 59:case 57:case 58:case 104:case 3:case 1:case 20:case 2:case 28:case 92:case 46:case 64:case 24:case 16:case 4:case 18:case 34:case 9:case 26:case 27:case 8:case 60:case 40:case 63:case 41:case 7:case 10:case 6:case 17:case 15:case 29:case 5:case 22:case 23:case 122:case 19:case 30:case 32:case 43:case 61:case 21:case 38:case 11:case 36:case 37:case 115:case 31:case 14:case 99:case 118:case 117:case 33:case 25:case 35:case 12:case 79:case 103:return true; +default:return false}}function PWb(a){var b;b=eXb(a);if(xWb(a,a.f))switch(xWb(a,a.f)?zsc(a.b,a.f):0){case 47:switch(xWb(a,a.f+1)?zsc(a.b,a.f+1):0){case 47:_Wb(a,(e$b(),c$b));return true;case 42:UWb(a);return true}break;case 60:if((xWb(a,a.f+1)?zsc(a.b,a.f+1):0)==33&&(xWb(a,a.f+2)?zsc(a.b,a.f+2):0)==45&&(xWb(a,a.f+3)?zsc(a.b,a.f+3):0)==45){GWb(a,aTc,CE(xE(iW,1),mLc,1,5,[]));_Wb(a,(e$b(),c$b));return true}break;case 45:if(b&&(xWb(a,a.f+1)?zsc(a.b,a.f+1):0)==45&&(xWb(a,a.f+2)?zsc(a.b,a.f+2):0)==62){GWb(a, +aTc,CE(xE(iW,1),mLc,1,5,[]));_Wb(a,(e$b(),c$b));return true}break;case 35:if(a.f==0&&(xWb(a,a.f+1)?zsc(a.b,a.f+1):0)==33){_Wb(a,(e$b(),d$b));return true}}return false}function SUb(a,b){var c,d,e,f,g,h,i;e=DWb(a.g,0).d.b;d=null;if(AVb(a,(AZb(),aZb)))d=(i=DWb(a.g,0).d.b,iTb(a,aZb),new t1b(new Z1b(i,a.f),cVb(a,0)));else{f=DWb(a.g,0).e;if(iYb==f||a.b.d&&VSb(f)||!a.b.c&&USb(f)){d=wUb(a);if(AVb(a,UYb)){iTb(a,UYb);d=new v_b(new Z1b(e,a.f),d)}}else if(b!=2&&(zVb(a,0,HYb)||zVb(a,0,FYb)))d=UUb(a,0);else throw gZ(new Irc("parseParameterCalled() without confirming a parameter exists.")); +}g=null;h=null;if(AVb(a,NXb)){if(zVb(a,1,gZb)){iTb(a,NXb);g=GUb(a)}else g=(iTb(a,NXb),nVb(a));h=pTb(a,DWb(a.g,0).d.b)}if(b==0&&d.p!=(n1b(),Q0b)&&AVb(a,ZXb)){iTb(a,ZXb);c=KTb(a,1);d=new t$b(new Z1b(e,a.f),d,c)}!!g&&(d=new I1b(h,d,g));return d}function zdb(a,b){var c,d,e,f,g,h,i,j,k;if(!a.c)return false;i=glb(a.c,b);if(!i)return false;d=i.d;if(!d)for(k=(Cyc(),new Wzc((new Fzc(i.p)).b.Vd()));k.b.Id();){j=k.b.Jd();(j.g==(cmb(),_lb)||j.g==amb)&&(d=j)}if(!d)return false;f=d.c;g=H8b(f,(Hbc(),ybc));if(!!g&& +g.Kh())return false;c=f.g;e=null;if(c.n==(Afc(),Jdc))e=c;else{ppb();if(!!c&&(c.n==ufc||c.n==jec||c.n==gdc)&&f.n==rec)if(f.c)e=Sd(f.c);else return false;else if(c.n==Ccc&&c.c==f)e=Sd(c.c.f);else if(c.n==Eec&&!!f.c&&!f.c.f)e=Sd(f.c);else throw gZ(new Irc("Unexpected declaration format:\n"+E9b(c)));}if(e.n==Jdc){h=Sd(B8b(e,2));return!Ldb(new Mdb(a),h)}else return V8b(e)&&zdb(a,I8b(e))}function kB(b,c){var d,e,f,g,h,i,j;this.f=Sd(b);this.c=Sd(c);try{this.a=SB(c.length,(Itc(),Gtc))}catch(a){a=fZ(a);if(pF(a, +197)){f=a;throw gZ(new rnb("Illegal alphabet length "+c.length,f));}else throw gZ(a);}g=$wnd.Math.min(8,Src(this.a));try{this.d=8/g|0;this.b=this.a/g|0}catch(a$1){a$1=fZ(a$1);if(pF(a$1,197)){f=a$1;throw gZ(new rnb("Illegal alphabet "+gtc(c,0,c.length),f));}else throw gZ(a$1);}e=zE(wF,cMc,46,128,15,1);Kxc(e,e.length);for(i=0;i>24}this.e=e;j=zE(dZ,{1109:1,3:1},46,this.d,16,1);for(h=0;h< +this.b;h++)j[RB(h*8,this.a,(Itc(),Atc))]=true;this.g=j}function DKb(a,b){var c,d;if(b.n==(Afc(),Xcc)||b.n==hdc){c=b.c;if(c){d=b.g;while(d.n!=eec||!Esc(d.c.Wh(),c.Wh())){if(d.n==Jdc||d.n==Xec){a.d.Ah(x2b('undefined label "%s"',CE(xE(iW,1),mLc,1,5,[c.Wh()])),a.q,T9b(b.k),S9b(b.k));break}d=d.g}d.n==eec&&Esc(d.c.Wh(),c.Wh())&&b.n==hdc&&!NKb(d.c?d.c.i:null)&&a.d.Ah("continue can only use labeles of iteration statements",a.q,T9b(b.k),S9b(b.k))}else if(b.n==hdc){d=b.g;while(!NKb(d)){if(d.n==Jdc||d.n==Xec){a.d.Ah("continue must be inside loop", +a.q,T9b(b.k),S9b(b.k));break}d=d.g}}else{d=b.g;while(!MKb(d)){if(d.n==Jdc||d.n==Xec){a.d.Ah("unlabelled break must be inside loop or switch",a.q,T9b(b.k),S9b(b.k));break}d=d.g}}}}function vGb(a,b,c){sGb();var d,e,f,g,h,i,j,k;k=tGb();g=new v9;o9(g,(uab(),mab));g.Pb=new Ie(Sd((Nqc(),true)));switch(c){case rRc:case "es9":h=(iQb(),eQb);break;case "es8":h=(iQb(),cQb);break;case "es7":h=(iQb(),aQb);break;case kOc:h=(iQb(),$Pb);break;case "es5":h=(iQb(),ZPb);break;default:h=(iQb(),YPb)}p9(g,sQb(h,(IRb(), +iRb),CE(xE(FR,1),sLc,24,0,[])));g.cb=new Ie(Sd(false));g.ad=true;f=Vwb("externs.js","var window;",(scc(),qcc));j=Vwb(b,a,qcc);g.wc=true;g.Dc=true;h9(g,new gcb(rGb,(E2(),C2)));q9(g,(wFb(),uFb));d=new wGb(g);x7(d,new xGb);z6(d,(Bn(),new Wz(Sd(f))),new Wz(Sd(j)),g);i=r8(new l8(d));e=tGb();uGb("Transpiled "+b+" in "+(e-k)/1E3+" seconds");return i}function Qtb(){Qtb=JZ;RIc("com.google.javascript.jscomp.ProcessDefines");bn();pp(CE(xE(iW,1),mLc,1,5,["COMPILED","goog.DEBUG"]));Ptb=new Mcb("JSC_UNKNOWN_DEFINE_WARNING", +(E2(),D2),new Rtc("unknown @define variable {0}"));new Mcb("JSC_INVALID_DEFINE_TYPE_ERROR",B2,new Rtc("@define tag only permits literal types"));Otb=new Mcb("JSC_INVALID_DEFINE_INIT_ERROR",B2,new Rtc("illegal initialization of @define variable {0}"));new Mcb("JSC_NON_GLOBAL_DEFINE_INIT_ERROR",B2,new Rtc("@define variable {0} assignment must be global"));new Mcb("JSC_DEFINE_NOT_ASSIGNABLE_ERROR",B2,new Rtc("@define variable {0} cannot be reassigned due to code at {1}."));new Rtc("line {0} of {1}")} +function JWb(a,b,c){var d,e,f,g,h,i,j;j=new stc;j.a+=String.fromCharCode(c);e=c==92;d=false;h=e?1:0;c=xWb(a,a.f)?zsc(a.b,a.f):0;while(hXb(c)||c==92||c==123&&h==2||c==125&&d){c==92&&(e=true);(c==92||h>0)&&++h;c==123&&(d=true);if(c==125||h>=6&&!d){d=false;h=0}itc(j,yWb(a));c=xWb(a,a.f)?zsc(a.b,a.f):0}i=j.a;if(e){i=oXb(i);if(i==null){EWb(a,wWb(a,a.f),_Sc,CE(xE(iW,1),mLc,1,5,[]));return tWb(a,(AZb(),aYb),b)}}g=i.charCodeAt(0);if(!iXb(g)){EWb(a,ZSb(a.i.b,b),"Character '%c' (U+%04X) is not a valid identifier start char", +CE(xE(iW,1),mLc,1,5,[_qc(g),Yrc(g)]));return tWb(a,(AZb(),aYb),b)}f=RSb(i,a.g);if(f)return new NRb(f.b,$Sb(a.i.b,b,a.f));return new PRb($Sb(a.i.b,b,a.f),i)}function ljb(a,b){var c,d,e,f,g,h;if(b.n==(Afc(),zcc))kjb(a,b);else if(b.n==Ycc)mjb(a,b);else{Ld(b.n==xec,b);Kd(b.n==xec);c=g9b(b);d=hjb(a,b);d.a[0].n==zcc?e=axc(d,0):e=r9b(f3b(CE(xE(HU,1),IMc,7,0,[])),a.a);r8b(e,r9b(new J9b(Aec),a.f));f=d.a.length==0?e:r9b(l3b(r9b(x3b(e,(n8b(),new Wbc(bfc,VPc))),a.d),exc(d,zE(HU,IMc,7,0,0,1))),a.a);kQb((iQb(), +YPb),l9(a.c.G))&&Tjb(a.c,b,'"..." passed to a constructor (consider using --language_out=ES5)');g=ijb(r9b(z3b(ijb(r9b((ae(Hsc(nPc,_sc(46))==-1,HMc,nPc),n8b(),new Wbc(rec,nPc)),a.e),aNc),"bind",CE(xE(nW,1),uNc,2,6,[])),a.i),hPc);h=r9b(M3b(gjb(g,CE(xE(HU,1),IMc,7,0,[c,f])),CE(xE(HU,1),IMc,7,0,[])),b.d);I9b(h,b);j9b(b.g,b,h);w7(a.c,h)}}function Oub(a,b,c){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r;Kd(!!b);Ld(!!b.a,OQc);Sd(b.b);r=b.b;g=b.a;e=g.c?g.c.i:null;Td(e,g);i=k3b(CE(xE(HU,1),IMc,7,0,[]));r.d&&q8b(i,G9b(d_(PQc, +(m=new J9b((Afc(),lfc)),m)),e));r.b&&q8b(i,G9b(d_(QQc,W$((Kob(c),BMc))),e));for(k=(h=(new Wuc(r.c.a)).a.He().Vd(),new avc(h));k.a.Id();){j=(f=k.a.Jd(),f.Ve());if(a.e){p=$$((n=new J9b((Afc(),lfc)),n));E8b(b.a.g,(Hbc(),zbc))!=0||(p=$$(p))}else p=(n=new J9b((Afc(),ffc)),n);d=g_((o=x3b(p,(n8b(),new Wbc((Afc(),bfc),j))),o));v7(a.a,d);qpb(c.f,(IRb(),GQb));q=RQc+j;q8b(i,q3b((l=(ae(Hsc(q,_sc(46))==-1,HMc,q),new Wbc(rec,q)),l),d,gdc))}I9b(i,e);u8b(e,f9b(i));if(r.d||r.b||r.c.a.Yd()!=0){v7(a.a,g);qpb(c.f,(IRb(), +RQb))}}function mKb(a){var b,c,d,e,f,g;g=a.c;d=Jsc(g,_sc(47));c=Hsc(g,_sc(92));if(c==-1)return g.substr(1,d-1);e=new stc;f=1;while(c!=-1){e.a+=""+(g==null?jLc:g).substr(f,c-f);++c;b=g.charCodeAt(c);switch(b){case 94:case 36:case 92:case 47:case 46:case 42:case 43:case 63:case 40:case 41:case 91:case 93:case 123:case 125:case 124:case 45:case 98:case 66:case 99:case 100:case 68:case 102:case 110:case 112:case 80:case 114:case 115:case 83:case 116:case 117:case 118:case 119:case 87:case 120:case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:e.a+= +"\\";default:e.a+=String.fromCharCode(b)}f=c+1;c=Isc(g,_sc(92),f)}e.a+=""+(g==null?jLc:g).substr(f,d-f);return e.a}function Gxc(a,b){var c,d,e,f,g,h,i,j,k,l;if(a==null)return jLc;i=b.a.Je(a,b);if(i!=null)return"[...]";c=new hGc(iLc,"[","]");for(e=a,f=0,g=e.length;f=14&&k<=16)))if(b.a.se(d)){!c.a?c.a=new vtc(c.d):ptc(c.a,c.b);mtc(c.a,"[...]")}else{h=(d==null||Array.isArray(d)&&(l=yE(d),!(l>=14&&l<=16)),d);j=new oDc(b);eGc(c, +Gxc(h,j))}else pF(d,1109)?eGc(c,cyc(d)):pF(d,430)?eGc(c,Xxc(d)):pF(d,77)?eGc(c,Yxc(d)):pF(d,1119)?eGc(c,byc(d)):pF(d,129)?eGc(c,_xc(d)):pF(d,646)?eGc(c,ayc(d)):pF(d,1120)?eGc(c,$xc(d)):pF(d,1121)&&eGc(c,Zxc(d));else eGc(c,d==null?jLc:NZ(d))}return!c.a?c.c:c.e.length==0?c.a.a:c.a.a+(""+c.e)}function tGc(a,b,c){var d,e,f,g,h,i,j,k,l,m,n;if(!a.b)return false;g=null;m=null;i=new XGc(null,null);e=1;i.a[1]=a.b;l=i;while(l.a[e]){j=e;h=m;m=l;l=l.a[e];d=a.a.lf(b,l.d);e=d<0?0:1;d==0&&(!c.c||kFc(l.e,c.d))&& +(g=l);if(!(!!l&&l.b)&&!pGc(l.a[e]))if(pGc(l.a[1-e]))m=m.a[j]=wGc(l,e);else if(!pGc(l.a[1-e])){n=m.a[1-j];if(n)if(!pGc(n.a[1-j])&&!pGc(n.a[j])){m.b=false;n.b=true;l.b=true}else{f=h.a[1]==m?1:0;pGc(n.a[j])?h.a[f]=vGc(m,j):pGc(n.a[1-j])&&(h.a[f]=wGc(m,j));l.b=h.a[f].b=true;h.a[f].a[0].b=false;h.a[f].a[1].b=false}}}if(g){c.b=true;c.d=g.e;if(l!=g){k=new XGc(l.d,l.e);uGc(a,i,g,k);m==g&&(m=k)}m.a[m.a[1]==l?1:0]=l.a[!l.a[0]?1:0];--a.c}a.b=i.a[1];!!a.b&&(a.b.b=false);return c.b}function Qfc(a,b,c,d){var e, +f,g,h;if(!b)return false;else if(a==b)return true;if(a.si()&&b.si())return true;h=a.Bi();g=b.Bi();if(h||g)if(c==2)return true;else if(c==1)return h&&g;else if(h&&g&&a.ui()^b.ui())return false;if(!!a.Mi()&&!!b.Mi())return Noc(a.Mi(),b.Mi(),c,d);if(!!a.Ii()&&!!b.Ii())return ojc(a.Ii(),b.Ii(),c,d);if(!Onc(a.ki(),b.ki(),c,d,0))return false;if(d.c?a.yi()&&b.yi():!!a.Ji()&&!!b.Ji())return yhc(pF(a,48)?a:null,pF(b,48)?b:null,c,d);if(a.ui()&&b.ui()){f=(pF(a,48)?a:null).Xi();e=(pF(b,48)?b:null).Xi();if(f== +null&&e==null);else return f==e}if(!!a.Ki()&&!!b.Ki())return false;if(pF(a,138))return Qfc(a.e,b,c,d);if(pF(b,138))return Qfc(a,b.e,c,d);return false}function C6(a,b,c){var d,e,f,g,h,i,j,k,l,m,n,o;l=c||!a.G.ad;if(oEc(a.s,b)||!l)return a.w;j=Z2b("js/"+b+fOc);d=(!a.G&&_6(a,new v9),n=Vwb(" [synthetic:"+b+"] ",j,(scc(),qcc)),Bn(),new Wz(Sd(n)),o=new H8(n),r7(a,o.i,o),Sd(x8(o,a)));for(i=d.c;!!i&&i.n==(Afc(),Ddc)&&i.c.n==(Afc(),bfc);i=d.c){f=i.c.Wh();m=Te(Qe(Ye(new nc(32))),f);switch(m.a.ce(0)){case "use":break; +case lNc:C6(a,m.a.ce(1),c);break;default:throw gZ(new GD("Bad directive: "+f));}e9b(d,i)}SZ(a.T)&&(epb(a,d,new Qrb(a,false)),epb(a,d,new hob(new vob(H6(a),new j8(a),"jscomp_"+b+"_"))));h=d.c?d.c.i:null;for(e=d.c;e;e=e.f)Rqb(e,a);g=f9b(d);if(!g)return a.w;k=M6(a);!a.w?u8b(k,g):s8b(k,g,a.w);a.w=h;qEc(a.s,b,h);s7(a,G6(k));i7(a);return h}function ikb(a,b,c,d){var e,f,g,h,i,j,k,l,m,n,o,p,q,r,s;n=b.g;q=a.g+_Pc+(""+a.d.a.Q++);i=b.c;p=i.f;m=b.c?b.c.i:null;A8b(b);g=null;s=F9b((f=new J9b((Afc(),Vcc)),f),b); +h=F9b((e=new J9b(Vcc),e),b);switch(b.n.f){case 60:g=i;r8b(s,Uqb(pkb(p,d,q)));r8b(h,Uqb(pkb(m,d,q)));break;case 62:g=pkb(i,d,q);r8b(s,Uqb(pkb(m,d,q)));break;case 61:g=pkb(i,d,q);r8b(h,Uqb(pkb(m,d,q)));break;default:throw gZ(new Irc("Unexpected expression: "+b));}h.c?k=(Xd(H3b(g)),Xd(s.n==Vcc),Xd(h.n==Vcc),new O9b(Tdc,g,s,h)):k=B3b(g,s);H9b(k,b);if(d){r=I9b(Zqb(q,null),b);r9b(r.c,a.j);l=c.g;p8b(l,r,c);o8b(l,k,r);o=Yjb((ae(Hsc(q,_sc(46))==-1,HMc,q),n8b(),new Wbc(rec,q)),b.d);j9b(n,b,o)}else{Kd(n.n== +Ddc);j=n.g;j9b(j,n,k)}return k}function k2b(a,b,c){var d,e,f;a.b=b;a.a=c;if(!b.d&&!b.e&&!b.f&&!b.g&&!b.i&&!b.j&&!b.k&&b.p==-1&&b.n==-1)switch(b.b){case 115:if(a.a==null)return jLc;case 100:if(pF(a.a,132)||pF(a.a,156)){e=NZ(a.a);return e}}J2b(a.b,a.a);switch(b.b){case 66:case 98:f=(qF(a.a)?e=NZ(a.a):a.a==null?e=SNc:e=TNc,j2b(a,e,0));break;case 72:case 104:f=r2b(a);break;case 83:case 115:f=(d=etc(a.a),j2b(a,d,0));break;case 67:case 99:f=o2b(a);break;case 100:case 111:case 120:case 88:f=s2b(a);break; +case 65:case 97:case 69:case 101:case 102:case 71:case 103:f=q2b(a);break;case 37:f=j2b(a,"%",0);break;case 110:f=gMc;break;case 116:case 84:f=p2b(a);break;default:throw gZ(O2b(b));}Yqc(b.b)&&f!=null&&(f=$b(NZ(f)));return f}function zqb(a,b){var c,d,e,f;switch(a.n.f){case 92:return zqb(a.c,b);case 42:for(d=a.c;d;d=d.f)if(d.n!=(Afc(),tdc)&&!zqb(d,b))return false;return true;case 38:for(e=a.c;e;e=e.f)if(!zqb(e,b))return false;return true;case 43:for(f=a.c;f;f=f.f)if(f.n==(Afc(),mec)||rqb(f)){if(!b)return false}else if(f.n== +fdc){if(!zqb(f.c,b)||!zqb(f.c?f.c.i:null,b))return false}else if(f.n==_ec){if(!zqb((Xd(!!f.c&&!f.c.f),f.c),b))return false}else{Yd(f.n==cfc,f);if(!zqb((Xd(!!f.c&&!f.c.f),f.c),b))return false}return true;case 65:return b&&!(a.n==(Afc(),Jdc)&&iqb(a.g)&&a.n==Jdc&&Gqb(a.c));case 118:for(c=a.c;c;c=c.f)if(c.n==(Afc(),kfc))if(!zqb(c.c,b))return false;return true;default:return tqb(a)}}function aNb(a,b){var c,d,e,f,g,h,i,j;if(b!=(fOb(),XNb)){a.o=b;return CMb(a,"msg.jsdoc.missing.lp",a.j.g,lOb(a.j)),null}d= +QMb(a,(Afc(),Jdc));h=null;xNb(a);if(!OMb(a,bOb)){b=!a.o?mOb(a.j):GMb(a);e=true;if(b==eOb){j=a.j.n;g=Esc(FMc,j);f=Esc("new",j);if(g||f){if(OMb(a,ONb)){!a.o?mOb(a.j):GMb(a);xNb(a);c=zNb(a,g?lfc:xec,YMb(a,!a.o?mOb(a.j):GMb(a)));if(!c)return null;r8b(d,c)}else return CMb(a,"msg.jsdoc.missing.colon",a.j.g,lOb(a.j)),null;if(OMb(a,PNb)){!a.o?mOb(a.j):GMb(a);xNb(a);b=!a.o?mOb(a.j):GMb(a)}else e=false}}if(e){h=hNb(a,b);if(!h)return null}}!!h&&q8b(d,h);xNb(a);if(!OMb(a,bOb))return CMb(a,aSc,a.j.g,lOb(a.j)), +null;xNb(a);!a.o?mOb(a.j):GMb(a);i=jNb(a);if(!i)return null;else q8b(d,i);return d}function Vib(a,b){var c,d,e,f,g,h,i,j,k;d=(c=new J9b((Afc(),Vcc)),c);u8b(d,f9b(a.g));r8b(d,t3b(V3b(a.i.b)));g=v3b((ae(Hsc("",_sc(46))==-1,HMc,""),n8b(),new Wbc(rec,"")),R3b(CE(xE(HU,1),IMc,7,0,[(ae(Hsc(TPc,_sc(46))==-1,HMc,TPc),new Wbc(rec,TPc)),(ae(Hsc(SPc,_sc(46))==-1,HMc,SPc),new Wbc(rec,SPc)),(ae(Hsc(UPc,_sc(46))==-1,HMc,UPc),new Wbc(rec,UPc))])),d);k=new J9b(zcc);for(j=(h=(new Wuc(a.c.a)).a.He().Vd(),new avc(h));j.a.Id();){i= +(e=j.a.Jd(),e.Ve());q8b(k,V3b(i.a))}f=t3b(l3b(x3b((ae(Hsc(NPc,_sc(46))==-1,HMc,NPc),new Wbc(rec,NPc)),new Wbc(bfc,"registerAndLoadModule")),CE(xE(HU,1),IMc,7,0,[g,V3b(Kib($Eb(a.a.C,(!b.d&&!!b.i&&(b.d=L6(b.c,b.i)),b.d).i.a).a)),k])));q8b(a.g,I9b(f,a.g));v7(a.a,a.g);v7(a.a,g);Vob(b)}function Dfc(){Afc();return CE(xE(KU,1),sLc,10,0,[Uec,Tcc,Ucc,Rcc,xdc,vec,lec,iec,Rdc,Ldc,kec,Wec,tfc,vcc,efc,qec,pdc,oec,zdc,zec,Scc,Oec,wec,xec,ndc,pfc,Pdc,Odc,Ycc,rec,Cec,bfc,Aec,lfc,Edc,nfc,Zec,$ec,Rec,mfc,Zdc,aec,zcc, +Eec,ofc,Jec,edc,Ccc,Fcc,Gcc,Ecc,Jcc,Mcc,Occ,Dcc,Ncc,Lcc,Hcc,Kcc,Icc,Sdc,Hec,wcc,$dc,jdc,Jdc,Tdc,gfc,$cc,ldc,xfc,qdc,Fdc,Hdc,Xcc,hdc,ufc,yfc,adc,vfc,tdc,Vec,Vcc,eec,Ddc,Xec,Qdc,Yec,gdc,idc,fec,cfc,_cc,Acc,Fec,odc,bdc,cdc,mec,ffc,jec,Idc,Gdc,zfc,Pcc,Vdc,Xdc,Wdc,Ydc,Adc,Cdc,Bdc,pec,rdc,Sec,_ec,fdc,hfc,ifc,kfc,jfc,mdc,yec,dfc,Wcc,Dec,Kdc,Iec,sfc,ycc,Bec,wfc,Tec,sec,Gec,Qec,rfc,Bcc,Mdc,Ndc,xcc,Kec,afc,wdc,Pec,sdc,Qcc,ydc,gec,hec,ddc,bec,cec,dec,udc,vdc,Udc,qfc,kdc,nec,_dc,Zcc,tec,uec,Lec,Mec,Nec])}function Wib(a, +b,c,d){var e,f,g,h;switch(c.n.f){case 105:ajb(a,z8((!b.d&&!!b.i&&(b.d=L6(b.c,b.i)),b.d)),c);break;case 109:E8b(c,(n8b(),U7b))!=0?Yib(a,b,c,d):E8b(c,T7b)!=0?(e=c.c?c.c.i:null,f=C3b(new J9b((Afc(),tdc)),new J9b(tdc),e.Sh(false)),F9b(f,c),p8b(d,f,c),Wib(a,b,f,d),g=Sib(a,e.Wh()),k9b(c,G9b(t3b(l3b(x3b((ae(Hsc(UPc,_sc(46))==-1,HMc,UPc),new Wbc(rec,UPc)),new Wbc(bfc,"exportAllFrom")),CE(xE(HU,1),IMc,7,0,[(ae(Hsc(g,_sc(46))==-1,HMc,g),new Wbc(rec,g))]))),c)),Vob(b),undefined):!!c.c&&!!c.c.f&&c.c.f==(c.c? +c.c.i:null)?Zib(a,b,c,d):c.c.n==(Afc(),Cdc)?_ib(a,b,c,d):Xib(a,b,c,d);break;case 85:Xd(a.g==c);h=c.c;Xd(h.n==(Afc(),pec));Sd(h.g);e9b(h.g,h);u8b(c,f9b(h));Pib(a);Oib(a);Vib(a,b);break;case 29:Uib(a,b,c)}}function dVb(a){var b,c,d;if(zVb(a,0,(AZb(),lYb))&&!zVb(a,1,GYb))return yUb(a);if(zVb(a,0,bYb))return hUb(a,false);if(zVb(a,0,oYb))return CUb(a);if(zVb(a,0,YXb))return dUb(a);if(zVb(a,0,rZb)&&(c=DWb(a.g,1).d.b.b>DWb(a.g,0).d.a.b,!(c||zVb(a,1,ZYb)||zVb(a,1,KXb)||zVb(a,1,XXb)))&&zVb(a,1,iYb)&&zVb(a, +2,ZXb))return oVb(a);if(zVb(a,0,SXb)&&(d=DWb(a.g,1).d.b.b>DWb(a.g,0).d.a.b,!(d||zVb(a,1,ZYb)||zVb(a,1,KXb)||zVb(a,1,XXb)))&&(zVb(a,1,vZb)||zVb(a,1,sYb)||zVb(a,1,PXb)||zVb(a,1,gYb)||zVb(a,1,IXb)||zVb(a,1,YXb)||zVb(a,1,wYb)||zVb(a,1,xYb)))return zTb(a);if((zVb(a,0,wYb)||zVb(a,0,xYb))&&(b=DWb(a.g,1).d.b.b>DWb(a.g,0).d.a.b,!(b||zVb(a,1,ZYb)||zVb(a,1,KXb)||zVb(a,1,XXb)))&&zVb(a,1,iYb))return MUb(a,false);return hVb(a)}function _2(){_2=JZ;$2=new Mcb("JSC_SUSPICIOUS_SEMICOLON",(E2(),D2),new Rtc("If this if/for/while really shouldn''t have a body, use '{}'")); +V2=new Mcb("JSC_SUSPICIOUS_NAN",D2,new Rtc("Comparison against NaN is always false. Did you mean isNaN()?"));X2=new Mcb("JSC_SUSPICIOUS_IN",D2,new Rtc('Use of the "in" keyword on non-object types throws an exception.'));W2=new Mcb("JSC_SUSPICIOUS_INSTANCEOF_LEFT",D2,new Rtc('"instanceof" with left non-object operand is always false.'));Y2=new Mcb("JSC_SUSPICIOUS_LEFT_OPERAND_OF_LOGICAL_OPERATOR",D2,new Rtc("Left operand of {0} operator is always {1}."));Z2=new Mcb("JSC_SUSPICIOUS_NEGATED_LEFT_OPERAND_OF_IN_OPERATOR", +D2,new Rtc("Suspicious negated left operand of 'in' operator."))}function qhb(a,b,c,d){var e,f,g,h,i,j,k,l,m;f=jhb(a,z8b(b.c.f));m=b.c;e=b.c.f;if(E8b(m,(Hbc(),gbc))==0&&E8b(e,gbc)==0){o8b(b,f,m);c9b(b,gbc,0);whb(a,b);return}if(z8b(m).n==(Afc(),ufc)){Xd(E8b(m,gbc)==0);k=m;Xd(!k.c.c);m=k.c.Sh(false)}else k=F9b(new J9b(ufc),m);h=F9b(Phb(a.a,"$jscomp$generator$forin$"+(""+(new j8(a.f.b)).a.Q++)),m);r8b(h,Chb(a.a,m,"forIn",CE(xE(HU,1),IMc,7,0,[f])));l=a.f.g?bgc(h.c.d):null;h.d=l;q8b(k,h);i=F9b(Yjb(x3b(h.Sh(false), +F9b((n8b(),new Wbc(bfc,"getNext")),f)),a.f.g?l.Wi("getNext"):null),f);g=F9b(Yjb(L3b(F9b(Yjb(h3b(Yjb(m,a.f.d),F9b(Yjb(l3b(i,CE(xE(HU,1),IMc,7,0,[])),a.f.d),f)),a.f.d),f),F9b(Yjb(new J9b(Aec),a.f.c),h)),a.f.a),f);o9b(g,E8b(m,gbc)!=0);j=F9b(u3b(k,g,F9b(new J9b(tdc),b),z8b(e)),b);phb(a,j,c,d)}function Jrb(a,b,c,d){var e,f,g,h,i,j,k,l,m,n,o,p,q,r,s;for(e=b.c;e;e=s){s=e.f;j=!c?e:c;k=!c?b:d;switch(e.n.f){case 83:Jrb(a,e,j,k);break;case 73:case 101:case 102:h=e.c;if(h.n==(Afc(),ufc)){l=h.c;if(l.n==odc){m= +Bpb(l);for(o=new wxc(m);o.a0){b=a.charCodeAt(0);if(b==45||b==43){a=a.substr(1);--f;i=b==45}}if(f==0)throw gZ(new usc(ITc+j+'"'));while(a.length>0&&a.charCodeAt(0)==48){a=a.substr(1);--f}if(f>(tsc(),rsc)[10])throw gZ(new usc(ITc+j+'"'));for(e=0;e0){l=-parseInt(a.substr(0,d),10);a=a.substr(d);f-=d;c=false}while(f>=g){d=parseInt(a.substr(0, +g),10);a=a.substr(g);f-=g;if(c)c=false;else{if(iZ(l,h)<0)throw gZ(new usc(ITc+j+'"'));l=rZ(l,k)}l=vZ(l,d)}if(iZ(l,0)>0)throw gZ(new usc(ITc+j+'"'));if(!i){l=sZ(l);if(iZ(l,0)<0)throw gZ(new usc(ITc+j+'"'));}return l}function Zdb(a,b,c){var d,e,f,g,h,i,j,k,l,m,n;dkb(a.c,c)==2&&gkb(a.c,c);m=c.g;l=(RFb(),Osc(Nsc(Nsc(Nsc(Nsc(Nsc(Nsc(Osc(Qsc(TFb((h=Osc(Osc(Osc(Osc(Osc(Nsc(Nsc(H8b(c,(Hbc(),ybc)).ug(),58,45),92,47)," ",cNc),"[",dNc),"]",eNc),"<",fNc),">",gNc),SFb(h))),"^\\./",""),"/","$"),92,36),64,36),43, +36),45,95),58,95),46,95),cNc,"_")+"$classdecl$var"+a.a++);j=(ppb(),k=Dpb(c),!k?null:H8b(k,mbc));n=Gpb(m,new jrb);g=c.d;Xd(true);e=r9b((ae(Hsc(l,_sc(46))==-1,HMc,l),n8b(),new Wbc((Afc(),rec),l)),g);f=x8b(e,false);j9b(m,c,f);d=I9b(q3b(e,c,gdc),c);qpb(b.f,(IRb(),RQb));q9b(d,k6b(E7b(j)));p8b(n.g,d,n);if(!!n&&(n.n==ufc||n.n==jec||n.n==gdc)&&!!n.c&&!n.c.f&&(Xd(!!n.c&&!n.c.f),n.c==m))Ydb(n);else if(n.n==Ddc){i=(Xd(!!n.c&&!n.c.f),n.c);i.n==Ccc&&V8b(i.c)&&i.c.f==f&&Ydb(i)}w7(a.b,d)}function aDb(){aDb=JZ;ZCb= +new Mcb("JSC_REFERENCE_BEFORE_DECLARE",(E2(),D2),new Rtc("Variable referenced before declaration: {0}"));$Cb=new Mcb("JSC_REDECLARED_VARIABLE",D2,new Rtc("Redeclared variable: {0}"));new Mcb("JSC_REFERENCE_BEFORE_DECLARE_ERROR",B2,new Rtc("Illegal variable reference before declaration: {0}"));new Mcb("JSC_REASSIGNED_CONSTANT",B2,new Rtc("Constant reassigned: {0}"));new Mcb("JSC_REDECLARED_VARIABLE_ERROR",B2,new Rtc("Illegal redeclared variable: {0}"));new Mcb("JSC_DECLARATION_NOT_DIRECTLY_IN_BLOCK", +B2,new Rtc("Block-scoped declaration not directly within block: {0}"));_Cb=new Mcb("JSC_UNUSED_LOCAL_ASSIGNMENT",C2,new Rtc("Value assigned to local variable {0} is never read"));Ur(MCc((Afc(),Tdc),CE(xE(KU,1),sLc,10,0,[Fdc,Hdc,Idc,Gdc,xfc])))}function thb(a,b,c,d){var e,f,g,h;Xd(I3b(b));Xd(!b.g);if(E8b(b,(Hbc(),gbc))==0){whb(a,b);return}switch(b.n.f){case 83:shb(a,b);break;case 82:mhb(a,b);break;case 84:ohb(a,b);break;case 76:xhb(a,b);break;case 0:r8b(b,bib(a.a,b,khb(a,jhb(a,g9b(b)))));eib(a.a,b); +a.a.j.e=false;break;case 39:r8b(b,khb(a,jhb(a,g9b(b))));eib(a.a,b);a.a.j.e=false;break;case 66:rhb(a,b,c);break;case 72:phb(a,b,c,d);break;case 73:qhb(a,b,c,d);break;case 70:e=Thb(a.a,d);f=Thb(a.a,c);cib(a.a,e);g=khb(a,jhb(a,g9b(b)));h=g9b(b);eib(a.a,F9b(B3b(F9b(Yjb(Z3b((Afc(),zec),g),a.f.a),g),Ghb(a.a,f,true,b)),b));Yhb(a.a,f,e);thb(a,h,null,null);Vhb(a.a);gib(a.a,e,b);cib(a.a,f);break;case 71:nhb(a,b,c,d);break;case 44:vhb(a,b,c);break;case 67:uhb(a,b,c);break;default:ae(false,"Unsupported token: %s ", +b.n)}}function mvb(a,b,c){var d,e,f,g,h,i,j,k,l,m;if(!(!!c.c&&!c.c.f)||c.c.n!=(Afc(),Ddc)){u7(a.a,Lmb(c,jvb,CE(xE(nW,1),uNc,2,6,[])));return}j=z8b(c.c.c);d=c.c;!!d&&e9b(c,d);k=UFb(z8((!b.d&&!!b.i&&(b.d=L6(b.c,b.i)),b.d)).a);r8b(c,F9b(q3b(F9b((ae(Hsc(k,_sc(46))==-1,HMc,k),n8b(),new Wbc((Afc(),rec),k)),j),j,ufc),j));r8b(c,I9b(t3b(l3b(x3b((ae(Hsc(kNc,_sc(46))==-1,HMc,kNc),new Wbc(rec,kNc)),new Wbc(bfc,CNc)),CE(xE(HU,1),IMc,7,0,[new Wbc(bfc,k)]))),c));i=(!b.d&&!!b.i&&(b.d=L6(b.c,b.i)),b.d).a.sh().b;m= +VQc.length;if(Esc(i.substr(i.length-m,m),VQc)&&j.n==Eec){l=a.a.G.kc;for(h=xu(new Wzc(l.a.b.Vd()));h.Id();){g=h.Jd();f=Lpb(j,g);if(!!f&&(f.n==bfc||f.n==Eec)){e=Vsc(i,0,i.length-12);if(f.n==bfc){_tc(a.b,i,e+(""+f.Wh()));break}else if(f.n==Eec){Yd(Esc(g,"browser"),g);lvb(a,e,f)}}}}Vob(b)}function I6(a){var b,c,d,e,f,g,h,i,j,k,l,m;e=a.G.pb;j=a.G.B;b=new stc;if(e.a.a.Ud()&&j.a.a.Ud())return r8(new l8(a));if(!e.a.a.Ud()){Sd(a.o);Sd(a.v);for(g=Im(Dm(CE(xE(cW,1),mLc,15,0,[v8b(a.o),v8b(a.v)])));Ru(g);){f= +Su(g);d=(c=H8b(f,(Hbc(),ybc)),!c?null:c.ug());for(l=xu(new Wzc(e.a.b.Vd()));l.Id();){k=l.Jd();if((new RegExp("^("+k+")$")).test(d)){m="// "+d+gMc+(!a.G&&_6(a,new v9),F7(a,f,true));b.a+=m;break}}}if(b.a.length==0)throw gZ(new GD("No files matched any of: "+e));}if(!j.a.a.Ud()){for(i=new Guc(a.B?new eyc(a.B.a):null);i.b2){a.a.Bh(URc+occ(rSc,CE(xE(iW,1),mLc,1,5,[oSc])),a.b.ug(),a.d,a.c);mPb(a,(c=yPb.d,Pd(b.n==Ycc,iSc,b),B8b(b,2),c));return false}f=(Pd(e.n==Jdc,pSc,e),B8b(e,2));if(!gPb(a,f)){a.a.Bh(URc+occ(mSc,CE(xE(iW,1),mLc,1,5,[sSc])),a.b.ug(),a.d,a.c);return false}return true}function UOb(a,b){var c, +d,e,f,g;if(!JOb(a,b,(NPb(),zPb)))return false;if(!gPb(a,(Pd(b.n==(Afc(),Ycc),iSc,b),B8b(b,1)))){mPb(a,(c=zPb.d,Pd(b.n==Ycc,iSc,b),B8b(b,1),c));return false}if((Pd(b.n==Ycc,iSc,b),B8b(b,2)).n!=Jdc){lPb(a,(d=oSc,Pd(b.n==Ycc,iSc,b),B8b(b,2),d));mPb(a,(c=zPb.d,Pd(b.n==Ycc,iSc,b),B8b(b,2),c));return false}e=(Pd(b.n==Ycc,iSc,b),B8b(b,2));g=(Pd(e.n==Jdc,pSc,e),C8b(e.c.f));if(g<1){a.a.Bh(URc+occ(qSc,CE(xE(iW,1),mLc,1,5,[oSc])),a.b.ug(),a.d,a.c);mPb(a,(c=zPb.d,Pd(b.n==Ycc,iSc,b),B8b(b,2),c));return false}if(g> +1){a.a.Bh(URc+occ(rSc,CE(xE(iW,1),mLc,1,5,[oSc])),a.b.ug(),a.d,a.c);mPb(a,(c=zPb.d,Pd(b.n==Ycc,iSc,b),B8b(b,2),c));return false}f=(Pd(e.n==Jdc,pSc,e),B8b(e,2));if(!gPb(a,f)){a.a.Bh(URc+occ(mSc,CE(xE(iW,1),mLc,1,5,[sSc])),a.b.ug(),a.d,a.c);return false}return true}function Igc(a,b,c,d){var e,f,g,h,i,j,k,l,m,n,o,p,q;Sd(a);if(b.Bi())return true;if(b.mi())return true;if($fc(a,b,c.c))return true;if(b.Mi()){q=b.Mi();e=(Yoc(new Epc,q.b)&&Voc(q),q.b);for(g=0;g"));q9b(o,k6b(l))}I9b(o,c);Z8b(o);r8b(g,o);qpb(b.f,(IRb(),gRb));v7(a.a,(Xd(!!o.c&&!o.c.f),o.c));w7(a.a,o)}function jkb(a,b,c){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r;k=b.g;g=aqb(k)&&k.n!=(Afc(),Ccc)&&k.c==b;e=null;if(g&&(ppb(),b.n==(Afc(),Pdc)||b.n==Odc))for(i=(!b.c?(Cyc(),Cyc(),Byc):new Kbc(b.c)).Vd();i.Id();){h=i.Jd();if(h.n!=(Afc(),bfc)&&!(h.n==rec&&(fqb(h,a.e)||kDc(a.c,h.Wh())))){d=jkb(a,h,c);!e&&(e=d)}}o=(r=a.g+"_const"+_Pc+(""+a.d.a.Q++),jDc(a.c,r),r);l=F9b((ae(Hsc(o,_sc(46))==-1, +HMc,o),n8b(),new Wbc((Afc(),rec),o)),b);r9b(l,b.d);if(g){Yd(b.n==rec||(ppb(),b.n==Pdc||b.n==Odc),b);j=H9b(Yjb(new J9b(Rpb(k)),k.d),k);n=k.c?k.c.i:null;A9b(k,Ccc);j9b(k,n,j);r8b(j,l);q8b(j,n);p=x8b(b,false)}else if(b.n==_ec){j9b(k,b,r9b(F9b((Xd(H3b(l)),new L9b(_ec,l)),b),b.d));p=F9b((m=f3b(CE(xE(HU,1),IMc,7,0,[b])),m),(Xd(!!b.c&&!b.c.f),b.c))}else{j9b(k,b,l);p=b}q=Zqb(o,p);r9b(q.c,p.d);f=c.g;p8b(f,q,c);!e&&(e=q);Xd(e.n==ufc);return e}function Njb(a,b,c){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w, +A;o=U6(a.c);t=Wjb(c,o,45);d=Vjb(c,o,0,t);u=Wjb(c,o,27);A=Wjb(c,o,52);l=Wjb(c,o,33);v=b.c?b.c.i:null;h=Ljb(v,u,t,A,l);k=new H7b((xMb(),m=CNb("!ITemplateArray"),lNb(m,!m.o?mOb(m.j):GMb(m))),"");j=new C7b(false);s7b(j,k);s=Yjb(n3b(h,k6b(j)),u);f=Yjb(K3b("$jscomp$templatelit$"+(""+(new j8(a.c)).a.Q++)),u);w=I9b(q3b(f,s,(Afc(),ufc)),b);r=(ppb(),Gpb(b,new frb(Xec)));r8b(r,w);w7(a.c,w);if(Kjb(v))i=I9b(t3b((q=h3b(O$(f.Sh(false),"raw"),(p=Tqb(O$(f.Sh(false),"slice"),CE(xE(HU,1),IMc, +7,0,[])),p)),q)),b);else{n=Mjb(v,d,t);i=I9b(t3b((p=h3b(O$(f.Sh(false),"raw"),n),p)),b)}o8b(r,i,w);e=Yjb(l3b(g9b(b),CE(xE(HU,1),IMc,7,0,[f.Sh(false)])),b.d);for(g=v.c;g;g=g.f)g.n==jfc||q8b(e,g9b(g));I9b(e,v);b9b(e,(n8b(),W7b),e.c.n!=Pdc);j9b(b.g,b,e);Vob(a)}function ymb(a,b,c){var d,e,f,g,h,i,j;if(a.a&&H8b(c,(Hbc(),sbc))!=null){ptc(b,H8b(c,(Hbc(),sbc)));return}if(c.n==(Afc(),Qcc)){b.a+="!";ymb(a,b,c.c)}else if(c.n==ydc){ymb(a,b,c.c);b.a+="="}else if(c.n==Kec){b.a+="(";g=c.c?c.c.i:null;for(d=c.c;d;d= +d.f){ymb(a,b,d);d!=g&&(b.a+="|",b)}b.a+=")"}else if(c.n==sdc){b.a+=HNc;!!c.c&&c.c.n!=tdc&&ymb(a,b,c.c)}else if(c.n==afc)b.a+="*";else if(c.n==Pec){b.a+="?";!!c.c&&ymb(a,b,c.c)}else if(c.n==Jdc)xmb(a,b,c);else if(c.n==hec){b.a+="{";i=c.c;h=i.c?i.c.i:null;for(e=i.c;e;e=e.f){if(e.c){ptc(ptc(b,e.c.Wh()),":");ymb(a,b,e.c?e.c.i:null)}else ptc(b,e.Wh());e!=h&&(b.a+=",",b)}b.a+="}"}else if(c.n==vfc)b.a+=XNc;else if(c.n==pfc){b.a+="typeof ";ymb(a,b,c.c)}else if(c.c){ptc(ptc(b,c.Wh()),"<");d=c.c;f=d.c?d.c.i: +null;for(j=d.c;j;j=j.f){ymb(a,b,j);j!=f&&(b.a+=",",b)}b.a+=">"}else ptc(b,c.Wh())}function YUb(a){var b,c,d,e,f,g,h,i;switch(DWb(a.g,0).e.f){case 29:return WTb(a,true,false);case 35:return b=DWb(a.g,0).d.b,iTb(a,(AZb(),hZb)),new x1b(new Z1b(b,a.f));case 21:return c=DWb(a.g,0).d.b,iTb(a,(AZb(),mZb)),new C1b(new Z1b(c,a.f));case 34:return d=DWb(a.g,0).d.b,iTb(a,(AZb(),lYb)),iTb(a,GYb),e=KTb(a,1),iTb(a,LXb),RVb(a,(IRb(),TQb)),new v$b(new Z1b(d,a.f),e);case 2:case 108:case 109:case 110:case 111:return wUb(a); +case 100:case 101:case 98:case 99:case 97:return GUb(a);case 107:case 104:return mVb(a,null);case 49:return DWb(a.g,1).e==(AZb(),fYb)?$Tb(a,HYb,MXb):GTb(a);case 45:return PUb(a);case 47:return bUb(a);case 94:case 95:return f=DWb(a.g,0).d.b,g=(i=zWb(a.g),a.f=i.d.a,i),new j_b(new Z1b(f,a.f),g);default:return h=DWb(a.g,0).d.b,wTb(a),VVb(a,"primary expression expected",CE(xE(iW,1),mLc,1,5,[])),new n_b(new Z1b(h,a.f))}}function fgb(a,b,c){var d,e,f,g,h,i,j,k;if(b.n==(Afc(),fdc)&&E8b(b,(Hbc(),zbc))!=0){Ujb(a.a, +b,"Static computed property");return}if(b.n==fdc&&!V8b(b.c)){Tjb(a.a,b.c,"Computed property with non-qualified-name key");return}k=bgb(b);Zfb(a,c,b);j=E8b(b,(Hbc(),zbc))!=0?c.b:c.j;d=new i1;if(b.n==fdc){Xd(E8b(b,zbc)==0);i=I8b(b.c);g1(d,(pgb(),mgb))}else if(b.Yh()){i=b.Wh();g1(d,(pgb(),ogb))}else{i=b.Wh();g1(d,(pgb(),ngb))}h1(d,i);e=pEc(j,i);f=!e?null:M4b(e.a);if(!!e&&!!k&&!(!!k&&k.a.Xh(f.a,false,true,false,false)))u7(a.a,Lmb(b,Vfb,CE(xE(nW,1),uNc,2,6,[i])));else{h=new C7b(false);if(!!H8b(b,mbc)&& +(H8b(b,mbc).a&1024)!=0){G6b(h);y7b(h,(e6b(),d6b))}!!H8b(b,mbc)&&(H8b(b,mbc).a&64)!=0?e7b(h):!k&&(k=new H7b(new J9b(Pec),(g=H8b(b,ybc),!g?null:g.ug())));!!k&&s7b(h,new H7b(x8b(k.a,false),k.b));E8b(b,zbc)!=0&&b.n!=fdc&&$6b(h);f1(d,k6b(h));qEc(j,i,e1(d))}}function VDc(){function e(){this.obj=this.createObject()}e.prototype.createObject=function(a){return Object.create(null)};e.prototype.get=function(a){return this.obj[a]};e.prototype.set=function(a,b){this.obj[a]=b};e.prototype[ISc]=function(a){delete this.obj[a]}; +e.prototype.keys=function(){return Object.getOwnPropertyNames(this.obj)};e.prototype.entries=function(){var b=this.keys();var c=this;var d=0;return{next:function(){if(d>=b.length)return{done:true};var a=b[d++];return{value:[a,c.get(a)],done:false}}}};if(!TDc()){e.prototype.createObject=function(){return{}};e.prototype.get=function(a){return this.obj[":"+a]};e.prototype.set=function(a,b){this.obj[":"+a]=b};e.prototype[ISc]=function(a){delete this.obj[":"+a]};e.prototype.keys=function(){var a=[];for(var b in this.obj)b.charCodeAt(0)== +58&&a.push(b.substring(1));return a}}return e}function Apb(a,b){switch(a.n.f){case 47:return tqb(a.c?a.c.i:null)||b.Ld(a)&&Apb(a.c?a.c.i:null,b);case 46:return Apb(a.c?a.c.i:null,b);case 62:case 61:return Apb(a.c,b)&&Apb(a.c?a.c.i:null,b);case 60:return Apb(a.c.f,b)&&Apb(a.c?a.c.i:null,b);case 33:case 99:return b.Ld(a);case 29:return tqb(a)||b.Ld(a);case 27:case 26:return b.Ld(a);case 28:return Yd(a.n==(Afc(),Ycc)||a.n==hfc,a),(E8b(a,(Hbc(),wbc))&16)>0||Kqb(a)||b.Ld(a);case 117:return Yd(a.n==(Afc(), +Ycc)||a.n==hfc,a),(E8b(a,(Hbc(),wbc))&16)>0||b.Ld(a);case 23:return Yd(a.n==(Afc(),xec),a),Q9b(E8b(a,(Hbc(),wbc))&15,13)||b.Ld(a);case 24:case 63:case 64:case 96:case 65:case 38:case 80:case 42:case 43:case 118:return true;case 92:return Apb(a.c,b);case 115:case 103:case 104:return false;default:if(aqb(a)||Hqb(a.n)||tqb(a))return true;throw gZ(new Irc("Unexpected expression node: "+a+"\n parent:"+a.g));}}function $qb(a){ppb();switch(a.f){case 1:return"|";case 61:return"||";case 2:return"^";case 62:return"&&"; +case 3:return"&";case 36:return"===";case 4:return"==";case 19:return"!";case 5:return"!=";case 37:return"!==";case 10:return"<<";case 40:return"in";case 7:return"<=";case 6:return"<";case 12:return">>>";case 11:return">>";case 9:return">=";case 8:return">";case 15:return"*";case 16:return"/";case 17:return"%";case 18:return"**";case 20:return"~";case 13:case 21:return"+";case 14:case 22:return"-";case 47:return"=";case 48:return"|=";case 49:return"^=";case 50:return"&=";case 51:return"<<=";case 52:return">>="; +case 53:return">>>=";case 54:return"+=";case 55:return"-=";case 56:return"*=";case 59:return"**=";case 57:return"/=";case 58:return"%=";case 79:return XNc;case 25:return yQc;case 41:return zQc;default:return null}}function ZTb(a,b){var c,d,e,f,g,h,i,j,k,l;h=!!mTb(a,(AZb(),bZb));if(KVb(a,0)){l=DWb(a.g,0).e;if(iYb==l||(OSb(),OSb(),!!Po(pSb,l))){k=null;j=kTb(a);TSb(j.a,false)&&RVb(a,(IRb(),eRb))}else{j=null;k=GUb(a)}}else{if(a.b.d&&zVb(a,0,HYb)&&zVb(a,1,iYb)&&zVb(a,2,NXb)){g=BUb(a);nTb(a);return g}k= +_Tb(a);j=null}if(!a.b.d||AVb(a,GYb)||AVb(a,EYb)){if(!k){i=(Y$b(),X$b);c=b.a}else{i=(Y$b(),W$b);c=null}if(b.b){f=KUb(a,b.d,j,b.c,h,b.a);nTb(a)}else{d=J$b(S$b(P$b(new T$b(i),j),b.c),c);sUb(a,d,h?(pWb(),nWb):(pWb(),oWb));f=I$b(d,pTb(a,b.d))}return i==(Y$b(),X$b)?f:new n$b(pTb(a,b.d),b.a,k,f)}else{h&&VVb(a,"Member variable cannot be prefixed by '*' (generator function)",CE(xE(iW,1),mLc,1,5,[]));e=sTb(a);AVb(a,ZXb)&&VVb(a,"Member variable initializers ('=') are not supported",CE(xE(iW,1),mLc,1,5,[])); +nTb(a);return!k?new m_b(pTb(a,b.d),j,b.c,false,b.a,e):new m$b(pTb(a,b.d),k,b.c,b.a,e)}}function Mhb(a,b){var c,d,e;Uhb(a);(a.a.a.length==2||a.a.a.length==3)&&q8b(b,I9b(B3b(Yjb(s3b(F9b(Yjb(x3b(F9b(Yjb(K3b(OPc+(a.r.b==0?"":"$"+a.r.b)),a.g),b),F9b((n8b(),new Wbc((Afc(),bfc),QPc)),b)),a.r.f.g?a.g.Wi(QPc):null),b),Yjb(new tac(1),a.r.f.e)),a.r.f.a),axc(a.a,0).a),b));a.a.a.length==2&&q8b(b,I9b(B3b(Yjb(L3b(F9b(Yjb(x3b(F9b(Yjb(K3b(OPc+(a.r.b==0?"":"$"+a.r.b)),a.g),b),F9b((n8b(),new Wbc((Afc(),bfc),QPc)),b)), +a.r.f.g?a.g.Wi(QPc):null),b),Yjb(O3b(a.a.a[1].c),a.r.f.e)),a.r.f.a),axc(a.a,0).a),b));if(a.a.a.length==1){t8b(b,f9b(axc(a.a,0).a));Ihb(a);return}e=F9b(Y3b(F9b(Yjb(x3b(F9b(Yjb(K3b(OPc+(a.r.b==0?"":"$"+a.r.b)),a.g),b),F9b((n8b(),new Wbc((Afc(),bfc),QPc)),b)),a.r.f.g?a.g.Wi(QPc):null),b),CE(xE(HU,1),IMc,7,0,[])),b);q8b(b,e);for(d=new wxc(a.a);d.aa.f.b,i||zVb(a,0,ZYb)||zVb(a,0,KXb)||zVb(a,0,XXb)))&&nTb(a);return new y$b(new Z1b(l,a.f),f,g,c,d,j)}function bXb(a){var b,c,d,e,f;yWb(a);if(!xWb(a,a.f)){FWb(a,"Unterminated string literal escape sequence",CE(xE(iW,1),mLc,1,5,[]));return false}if(mXb(xWb(a, +a.f)?zsc(a.b,a.f):0)){d=yWb(a);d==13&&(xWb(a,a.f)?zsc(a.b,a.f):0)==10&&yWb(a);return true}e=yWb(a);switch(e){case 39:case 34:case 96:case 92:case 98:case 102:case 110:case 114:case 116:case 118:case 48:return true;break;case 120:c=SWb(a)&&SWb(a);c||FWb(a,bTc,CE(xE(iW,1),mLc,1,5,[]));return c;case 117:if((xWb(a,a.f)?zsc(a.b,a.f):0)==123){yWb(a);if((xWb(a,a.f)?zsc(a.b,a.f):0)==125){FWb(a,cTc,CE(xE(iW,1),mLc,1,5,[]));return false}b=true;while((xWb(a,a.f)?zsc(a.b,a.f):0)!=125&&b)b=b&&SWb(a);b||FWb(a, +bTc,CE(xE(iW,1),mLc,1,5,[]));yWb(a);return b}else{f=SWb(a)&&SWb(a)&&SWb(a)&&SWb(a);f||FWb(a,bTc,CE(xE(iW,1),mLc,1,5,[]));return f}}if(e==47);else GWb(a,"Unnecessary escape: '\\%s' is equivalent to just '%s'",CE(xE(iW,1),mLc,1,5,[_qc(e),_qc(e)]));return true}function BLb(a,b){var c,d,e,f,g,h,i,j,k,l,m,n,o;g=b.j==(Y$b(),V$b);i=b.j==X$b;e=b.j==U$b;f=b.e;h=b.f;j=b.c.p==(n1b(),b0b);h&&bKb(a.a,b,(IRb(),$Qb));i&&bKb(a.a,b,(IRb(),gRb));e&&bKb(a.a,b,(IRb(),GQb));f&&bKb(a.a,b,(IRb(),HQb));h&&f&&bKb(a.a,b,(IRb(), +IQb));l=b.k;if(l)m=MLb(a,l);else{if(g||i){a.a.d.Ah("unnamed function statement",a.a.q,(RJb(),b.o.b.b+1),b.o.b.a);m=iKb(a.a,(Afc(),rec),"__missing_name__")}else m=iKb(a.a,(Afc(),rec),"");T9b(m.k)==-1&&vKb(m,b.o.b,b.o.a)}n=dKb(a.a,(Afc(),Jdc));i&&m._h("");q8b(n,m);aLb(a,n.c,b.d);q8b(n,yKb(a.a,b.b));bLb(a,n,b.n);d=yKb(a.a,b.c);if(!e&&!j&&d.n!=Vcc){Xd(a.a.a.f==(FJb(),DJb));d=(c=new J9b(Vcc),c)}hLb(d);q8b(n,d);c9b(n,(Hbc(),Xac),h?1:0);Xd(n.n==Jdc);c9b(n,Fac,e?1:0);Xd(n.n==Jdc);c9b(n,Gac,f?1:0);b9b(n,(n8b(), +f8b),b.g);if(i){T9b(n.k)==-1&&vKb(n,b.o.b,b.o.a);k=iKb(a.a,mec,l.a);q8b(k,n);x9b(k,b.i);_Kb(a,b,k,b.a);n9b(n,H8b(n,Nac));vKb(k,l.d.b,l.d.a);o=k}else o=n;return o}function hkb(a,b,c){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,A,B;k=skb(c,b);h=(ppb(),upb(k,false,a.b));e=rkb(k);o=new vkb;o.b=h;o.a=e;for(g=null,d=k,m=d.g;m!=b;g=d,d=m,m=d.g){Xd(!ukb(m)||d==m.c);if(m.n==(Afc(),Ccc))if(lkb(a,m,o.b));else{i=m.c;p=i.n;if(i!=d){Xd(i.n==Pdc||i.n==Odc);p==Odc&&fkb(a,i.c?i.c.i:null,null,o);fkb(a,i.c,null,o)}}else if(m.n== +Ycc&&qqb(m.c)){f=m.c;fkb(a,f.f,d,o);if(kkb(a,f,o.b)&&f.c!=g){Yd(a.a,"Object method calls can not be decomposed.");o.b=true;n=(Ld(m.n==Ycc,m),q=m.c,Ld(q.n==Pdc||q.n==Odc,q),r=q.d,s=null,!!r&&(s=r.Ii()?yjc(r.Ii(),ePc):a.i),t=jkb(a,q,o.a),o.a=t,u=t.c.c,Ld(u.n==Pdc||u.n==Odc,u),v=jkb(a,u.c,o.a),o.a=v,w=v.c,A=t.c,B=I9b(l3b(Yjb(x3b(A.Sh(false),Yjb((n8b(),new Wbc(bfc,ePc)),a.f)),s),CE(xE(HU,1),IMc,7,0,[w.Sh(false)])),m),r9b(B,m.d),g9b(m),!!m.c&&t8b(B,f9b(m)),j9b(m.g,m,B),B);m=n}}else m.n==Eec?ekb(a,m.c, +d,o):fkb(a,m.c,d,o)}if(k==c);else{l=k.g;j=l.n!=(Afc(),Ddc);ikb(a,k,e,j)}}function qwb(a){kwb();this.a=a;this.b=(oo(),es(new hs,YIc($Qc),fwb)).Lf(nwb('Duplicate parameter name "{0}"'),Uvb).Lf(YIc("Unnecessary escape:.*"),hwb).Lf(YIc("^invalid param name.*"),Yvb).Lf(nwb(occ(_Qc,CE(xE(iW,1),mLc,1,5,[]))),Tvb).Lf(YIc("^Keywords and reserved words are not allowed as unquoted property.*"),Wvb).Lf(YIc("^Too many template parameters"),ewb).Lf(YIc(".*Type annotations should have curly braces.*"),Zvb).Lf(YIc("Missing type declaration\\."), +$vb).Lf(YIc(".*Unknown type.*"),iwb).Lf(YIc("^Bad type annotation.*"),gwb).Lf(YIc("Too deep recursion while parsing"),cwb).Lf(YIc("^Octal .*literal.*"),Xvb).Lf(YIc("^String continuations.*"),dwb).Lf(YIc("^This language feature is only supported for .*"),_vb).Lf(YIc("^This language feature is not currently supported by the compiler: .*"),jwb).Lf(YIc("^type syntax is only supported in ES6 typed mode.*"),Vvb).Lf(YIc("^Can only have JSDoc or inline type.*"),awb).Kf()}function ZUb(a){var b,c,d,e,f,g;f= +DWb(a.g,0).e;if(f==(AZb(),bZb))return $Ub(a);else if(AVb(a,aZb)){RVb(a,(IRb(),mRb));return jVb(a)}else if(f==gZb||f==DYb||f==iYb||(OSb(),!!Po(pSb,f)))return zVb(a,0,iYb)&&Esc(DWb(a.g,0).a,nNc)&&(KVb(a,1)||DWb(a.g,1).e==HYb)?uUb(a,new sWb(DWb(a.g,0).d.b)):zVb(a,0,iYb)&&Esc(DWb(a.g,0).a,"set")&&(KVb(a,1)||DWb(a.g,1).e==HYb)?eVb(a,new sWb(DWb(a.g,0).d.b)):zVb(a,0,iYb)&&Esc(DWb(a.g,0).a,GNc)&&(c=DWb(a.g,1).d.b.b>DWb(a.g,0).d.a.b,!(c||zVb(a,1,ZYb)||zVb(a,1,KXb)||zVb(a,1,XXb)))&&(KVb(a,1)||DWb(a.g,1).e== +HYb||zVb(a,1,bZb)&&(KVb(a,2)||DWb(a.g,2).e==HYb))?OTb(a,new sWb(DWb(a.g,0).d.b)):DWb(a.g,1).e==GYb?ZTb(a,new sWb(DWb(a.g,0).d.b)):_Ub(a);else if(f==HYb){e=DWb(a.g,0).d.b;d=_Tb(a);if(AVb(a,NXb)){iTb(a,NXb);g=KTb(a,1);return new k$b(new Z1b(e,a.f),d,g)}else{b=new T$b((Y$b(),W$b));sUb(a,b,(pWb(),oWb));g=I$b(b,new Z1b(e,a.f));return new n$b(new Z1b(e,a.f),null,d,g)}}else throw gZ(new GD(MLc));}function Hgb(a,b,c,d,e,f){var g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,A,B,C;if(a.f==(Wgb(),Vgb))if(swc(a.e)||!uwc(a.e).a)return; +B=LPc+a.b++;k=S$((j=z8b(d),Kob(b),j));A=(t=(ae(Hsc(B,_sc(46))==-1,HMc,B),n8b(),new Wbc((Afc(),rec),B)),t);w=q3b(Tbc(A,false),k,ufc);I9b(w,c);p8b(f.g,w,f);for(h=c.c;h;h=o){o=h.f;if(h.n==tdc){p=t3b((s=Tqb(O$(Tbc(A,false),wPc),CE(xE(HU,1),IMc,7,0,[])),s));I9b(p,h);p8b(f.g,p,f);continue}if(h.n==mdc){r=LPc+a.b++;q=O$((u=Tqb(O$(Tbc(A,false),wPc),CE(xE(HU,1),IMc,7,0,[])),u),yPc);C=q3b((v=(ae(Hsc(r,_sc(46))==-1,HMc,r),new Wbc(rec,r)),v),q,ufc);I9b(C,h);p8b(f.g,C,f);l=z8b(h.c);n=Cgb((s=(ae(Hsc(r,_sc(46))== +-1,HMc,r),new Wbc(rec,r)),s),z8b(h.c?h.c.i:null))}else if(h.n==Sec){l=z8b(h.c);n=T$((i=Tbc(A,false),Kob(b),i))}else{l=z8b(h);n=O$((s=Tqb(O$(Tbc(A,false),wPc),CE(xE(HU,1),IMc,7,0,[])),s),yPc)}if(e.n==Ccc){g=(s=h3b(l,n),s);m=(Yd(H3b(g),g),new L9b(Ddc,g))}else m=q3b(l,n,e.n);I9b(m,c);p8b(f.g,m,f);Lgb(a,b,l,l.g)}z8b(f);Vob(b)}function lKb(a,b){var c,d,e,f,g,h,i,j;j=b.c;g=b.d;f=j.length;Xd(f>0);Xd(j.charCodeAt(0)!=45&&j.charCodeAt(0)!=43);if(j.charCodeAt(0)==46)return Brc("0"+j);else if(j.charCodeAt(0)== +48&&f>1)switch(j.charCodeAt(1)){case 46:case 101:case 69:return Brc(j);case 98:case 66:{aKb(a,b,(IRb(),JQb));i=0;d=1;while(++d=48&&e<=55)h=h*8+PKb(e);else{a.d.Ah(ORc,a.q,g.b.b+1,g.b.a); +return 0}}a.a.g==(KJb(),JJb)?a.d.Ah(PRc,a.q,g.b.b+1,g.b.a):a.d.Bh(PRc,a.q,g.b.b+1,g.b.a);return h;case 56:case 57:a.d.Ah(ORc,a.q,g.b.b+1,g.b.a);return 0;default:throw gZ(new Irc("Unexpected character in number literal: "+nF(j.charCodeAt(1))));}else return Brc(j)}function U8b(a,b,c,d,e,f){var g,h,i,j,k,l,m,n,o,p;if(a.n!=b.n||C8b(a)!=C8b(b)||a.Fj!=b.Fj)return false;if(c&&!ugc(a.d,b.d))return false;if(e&&!C5b(H8b(a,(Hbc(),mbc)),H8b(b,mbc)))return false;p=H8b(a,(Hbc(),Nac));o=H8b(b,Nac);if((!!p||!!o)&& +(!p||!o||!U8b(p,o,c,d,e,false)))return false;if(a.n==(Afc(),$dc)||a.n==jdc){i=E8b(a,abc);j=E8b(b,abc);if(i!=j)return false}else if(a.n==bfc||a.n==cfc){if(a.n==cfc){k=E8b(a,ubc);l=E8b(b,ubc);if(k!=l)return false}m=E8b(a,xbc);n=E8b(b,xbc);if(m!=n)return false}else if(a.n==Ycc){if(E8b(a,Wac)!=0!=(E8b(b,Wac)!=0))return false}else if(a.n==Jdc){if((a.n==Jdc&&E8b(a,Fac)!=0)!=(b.n==Jdc&&E8b(b,Fac)!=0))return false;if(E8b(a,Xac)!=0!=(E8b(b,Xac)!=0))return false;if((a.n==Jdc&&E8b(a,Gac)!=0)!=(b.n==Jdc&&E8b(b, +Gac)!=0))return false}if(f){if(E8b(a,wbc)!=E8b(b,wbc))return false;if(E8b(a,lbc)!=0!=(E8b(b,lbc)!=0))return false}if(d)for(g=a.c,h=b.c;g;g=g.f,h=h.f)if(!g.Xh(h,c,d,e,f))return false;return true}function crb(a,b){ppb();var c,d;if(a.n==(Afc(),ofc)&&P8b(a)&&b==(a.c?a.c.i:null))$pb((Kd(a.n==ofc),a.c.f))?e9b(a,b):A8b(b);else if(b.n==adc){d=!b.g?null:b.g.g;Xd((Kd(d.n==ofc),P8b(d)));z8b(b)}else{c=b.g;if(c.n==ofc&&c.c.f==b){d=b.g;Xd((Kd(d.n==ofc),P8b(d)));A8b(b)}else if(b.n==Vcc)A8b(b);else if(a.n==Vec|| +a.n==Xec||a.n==Vcc||a.n==pec||b.n==$cc||b.n==ldc||b.n==mec)e9b(a,b);else if(!!a&&(a.n==ufc||a.n==jec||a.n==gdc)||a.n==Ddc)if(!!a.c&&!!a.c.f)e9b(a,b);else{e9b(a,b);crb(a.g,a)}else if(a.n==eec&&b==(a.c?a.c.i:null)){e9b(a,b);crb(a.g,a)}else if(a.n==Fdc)j9b(a,b,new J9b(tdc));else if(a.n==Fec)e9b(a,b);else if(a.n==Acc)b==(a.c?a.c.i:null)?e9b(a,b):j9b(a,b,new J9b(tdc));else if(a.n==odc){e9b(a,b);!!a.g.c&&crb(a.g,a)}else if(a.n==Sec)z8b(a);else if(a.n==Jec)e9b(a,b);else if(a.n==Vdc)if(b==a.c)j9b(a,b,new J9b(tdc)); +else throw gZ(new Irc("Invalid attempt to remove: "+b+" from "+a));else throw gZ(new Irc("Invalid attempt to remove node: "+b+" of "+a));}}function JUb(a){var b,c,d,e,f,g,h,i,j,k,l,m,n;f=DWb(a.g,0).d.b;zVb(a,0,(AZb(),iYb))&&Esc(DWb(a.g,0).a,GNc)&&(c=DWb(a.g,1).d.b.b>DWb(a.g,0).d.a.b,!(c||zVb(a,1,ZYb)||zVb(a,1,KXb)||zVb(a,1,XXb)))&&zVb(a,1,gYb)?e=(g=DWb(a.g,0).d.b,oTb(a,GNc),iTb(a,gYb),h=AVb(a,bZb),h&&iTb(a,bZb),i=K$b(P$b(new T$b((Y$b(),W$b)),(j=DWb(a.g,0).e,iYb==j||a.b.d&&VSb(j)||!a.b.c&&USb(j)?kTb(a): +null))),sUb(a,i,h?(pWb(),mWb):(pWb(),lWb)),I$b(i,new Z1b(g,a.f))):zVb(a,0,gYb)?e=(k=DWb(a.g,0).d.b,iTb(a,(OSb(),hSb).b),l=!!mTb(a,bZb),m=P$b(new T$b((Y$b(),W$b)),(n=DWb(a.g,0).e,iYb==n||a.b.d&&VSb(n)||!a.b.c&&USb(n)?kTb(a):null)),sUb(a,m,l?(pWb(),nWb):(pWb(),oWb)),I$b(m,new Z1b(k,a.f))):e=YUb(a);while(zVb(a,0,HYb)||zVb(a,0,MYb)||zVb(a,0,BYb)||zVb(a,0,jZb))switch(DWb(a.g,0).e.f){case 49:iTb(a,HYb);d=xTb(a,1);iTb(a,MXb);e=new l_b(new Z1b(f,a.f),e,d);break;case 51:iTb(a,MYb);b=kTb(a);e=new k_b(new Z1b(f, +a.f),e,b);break;case 107:case 104:e=mVb(a,e);break;default:throw gZ(new GD(MLc));}return e}function f0(a,b){var c,d,e,f,g,h,i,j,k,l,m;switch(b.n.f){case 86:F_(a,(IRb(),aRb),b);b0((Afc(),Qdc),b);c=Cfc(b.n);c!=-1&&(d=C8b(b),c!=d&&D0(QMc+c+RMc+d,b));g0(b);e=b.c;G_(a,e,false);e.c.Wh().length==0||D0(YMc,b);f=e.c.f;!!f.c&&D0("get methods must not have parameters.",b);return;case 87:F_(a,(IRb(),yRb),b);b0((Afc(),Yec),b);g=Cfc(b.n);g!=-1&&(h=C8b(b),g!=h&&D0(QMc+g+RMc+h,b));g0(b);i=b.c;G_(a,i,false);i.c.Wh().length== +0||D0(YMc,b);j=i.c.f;!!j.c&&!j.c.f||D0("set methods must have exactly one parameter.",b);return;case 91:b0((Afc(),cfc),b);g0(b);k=C8b(b);1!=k&&D0(UMc+k,b);E_(a,b.c);E8b(b,(n8b(),d8b))!=0&&F_(a,(IRb(),XQb),b);return;case 98:s_(a,b,false);E8b(b,(Hbc(),zbc))!=0&&D0("Keys in an object literal should not be static.",b);return;case 116:F_(a,(IRb(),PQb),b);b0((Afc(),fdc),b);l=Cfc(b.n);l!=-1&&(m=C8b(b),l!=m&&D0(QMc+l+RMc+m,b));E_(a,b.c);E_(a,b.c?b.c.i:null);return;case 115:p0(a,b);return;default:D0("Expected object literal key expression but was "+ +b.n,b)}}function Ngb(a,b,c,d){var e,f,g,h,i,j,k,l,m,n,o,p;ppb();if(!!d&&(d.n==(Afc(),ufc)||d.n==jec||d.n==gdc)&&!lqb(d.g))Jgb(a,b,c,c.f,d,d);else if(d.n==(Afc(),Ccc))d.g.n==Ddc?Jgb(a,b,c,c.f,d,d.g):(e=LPc+a.b++,f=(ae(Hsc(e,_sc(46))==-1,HMc,e),n8b(),new Wbc(rec,e)),g=z8b(d.c?d.c.i:null),h=q3b(Tbc(f,false),g,jec),qpb(b.f,(IRb(),fRb)),i=h3b(z8b(d.c),Tbc(f,false)),j=(Yd(H3b(i),i),new L9b(Ddc,i)),k=S3b(Tbc(f,false)),l=k3b(CE(xE(HU,1),IMc,7,0,[h,j,k])),m=N$(new J9b(Jec),l),Xd(m.n==Jdc),c9b(m,(Hbc(),Fac), +1),n=Tqb(m,CE(xE(HU,1),IMc,7,0,[])),qpb(b.f,GQb),I9b(n,d),b9b(n,W7b,true),j9b(d.g,d,n),Rqb(n,a.a),Jgb(a,b,i.c,i.c?i.c.i:null,i,j),undefined);else if(d.n==Sec||d.n==cfc||d.n==Acc||d.n==mdc||d.n==fdc);else if(d.n==Idc||d.n==Gdc||d.n==Hdc||lqb(d.g))Mgb(a,c);else if(d.n==adc){o=LPc+a.b++;p=c.f;k9b(c,(ae(Hsc(o,_sc(46))==-1,HMc,o),n8b(),new Wbc(rec,o)));r8b(p,q3b(c,(ae(Hsc(o,_sc(46))==-1,HMc,o),new Wbc(rec,o)),jec));qpb(b.f,(IRb(),fRb))}else throw gZ(new Irc("unexpected parent"));}function ebb(a){if(a.t!= +(E2(),C2)&&!a.s)throw gZ(new fbb("Cannot check use of goog.getCssName because of empty blacklist.",CE(xE(iW,1),mLc,1,5,[])));if(a.Mc&&!a.Lc)throw gZ(new fbb("remove_unused_prototype_props_in_externs requires remove_unused_prototype_props to be turned on.",CE(xE(iW,1),mLc,1,5,[])));if(a.Db==(Pab(),Oab)&&a.Zb!=-1)throw gZ(new fbb("max_function_size_after_inlining has no effect if inlining is disabled.",CE(xE(iW,1),mLc,1,5,[])));if(a.V){if(!kQb(l9(a),(iQb(),ZPb)))throw gZ(new fbb("Dart requires --language_out=ES5 or higher.", +CE(xE(iW,1),mLc,1,5,[])));a.f=false;a.bb=false}if(a.Mc&&a.ib)throw gZ(new fbb("remove_unused_prototype_props_in_externs and export_local_property_definitions cannot be used together.",CE(xE(iW,1),mLc,1,5,[])));if((uab(),jab)==a.Sb){X2b();if(Y2b(W2b,"/javascript/tools/jscompiler/allow-type-syntax.txt")==null)throw gZ(new fbb("type syntax is not yet generally available, please contact jscomp-team@",CE(xE(iW,1),mLc,1,5,[])));}}function Mnb(a,b){var c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s;Kd(b.n==(Afc(),Eec)); +k=new fxc;d=b.c;g=b.d;while(d)if(E8b(d,(n8b(),M7b))!=0||E8b(d,O7b)!=0){Ujb(a.b,d,"computed getter/setter in an object literal");return}else if(d.n==Qdc||d.n==Yec)d=d.f;else{e=d.f;e9b(b,d);k.Pd(d);d=e}f="$jscomp$compprop"+(""+(new j8(a.b)).a.Q++);k=pF(k,113)?Dn(k):pF(k,173)?k.a:pF(k,69)?new hx(k):new Zw(k);l=Yjb((ae(Hsc(f,_sc(46))==-1,HMc,f),n8b(),new Wbc(rec,f)),g);for(i=k.Vd();i.Id();){h=i.Jd();if(h.n==fdc){j=g9b(h);q=g9b(h);r=q.d;l=Yjb(o3b(Yjb(h3b(Nnb(a,w3b(Yjb((ae(Hsc(f,_sc(46))==-1,HMc,f),new Wbc(rec, +f)),g),j)),q),r),l),g)}else{p=g9b(h);r=p.d;A9b(h,bfc);r9b(h,a.d);o=h.Yh()?Odc:Pdc;c=Yjb(new N9b(o,Yjb((ae(Hsc(f,_sc(46))==-1,HMc,f),new Wbc(rec,f)),g),h),r);l=Yjb(o3b(Yjb(h3b(c,p),r),l),g)}}n=b;while(ppb(),!(n.n!=pec&&Jqb(n.g)))n=n.g;I9b(l,b);j9b(b.g,b,l);m=Wjb(a.a,a.c,14);s=q3b(Yjb((ae(Hsc(f,_sc(46))==-1,HMc,f),new Wbc(rec,f)),g),(Rjb(),!!m&&(b.d=m,b),b),ufc);I9b(s,n);p8b(n.g,s,n);w7(a.b,s)}function U8(a,b,c){var d,e,f;switch(b.n.f){case 28:if(!!b.c&&!!b.c.f&&b.c.f==(b.c?b.c.i:null)&&b.c.n==(Afc(), +Pdc)&&_8b(b.c.c,kNc)){_wc(a.d,(wDb(),vDb),0)!=-1||Ywc(a.d,vDb);e=b.c;d=b.c?b.c.i:null;switch((e.c?e.c.i:null).Wh()){case mNc:qGc(a.a,mNc,kNc);case CNc:if(d.n!=(Afc(),bfc))return;Ywc(a.c,d.Wh());return;case lNc:if(d.n!=(Afc(),bfc))return;Ywc(a.d,BDb(d.Wh()));return;case pNc:if(d.n!=(Afc(),bfc))return;Ywc(a.e,d.Wh());return;case BNc:b=d.c?d.c.i:null;break;default:return}}else if(c.n==(Afc(),Pdc)&&(a9b(c,oOc,oOc.length)||a9b(c,pOc,pOc.length))&&c.g.n==Ycc){d=c.g.c.f;if(d.n!=bfc)return;Ywc(a.c,d.Wh())}break; +case 112:if(E8b(c,(n8b(),Z7b))==0){Ywc(a.c,UFb(a.b.a));qGc(a.a,mNc,kOc)}break;case 105:T8(a,b.c?b.c.i:null,b);return;case 109:ppb();Kd(b.n==(Afc(),Adc));!!b.c&&!!b.c.f&&b.c.f==(b.c?b.c.i:null)&&T8(a,b.c?b.c.i:null,b);return;case 76:_8b(b.c,kNc)&&Dqb(b.c)&&Ywc(a.c,kNc);break;case 84:case 88:case 82:case 85:case 29:case 95:case 100:break;default:return}for(f=b.c;f;f=f.f)U8(a,f,b)}function NPb(){NPb=JZ;pPb=new OPb(COc,0,"all",0,0,0);qPb=new OPb("COND",1,"cond",3,3,1);rPb=new OPb("EQ",2,"eq",2,2,3);tPb= +new OPb("ISCTOR",3,"isCtor",1,1,3);uPb=new OPb("ISDEFINED",4,"isDefined",1,1,4);vPb=new OPb("ISRECORD",5,"isRecord",1,1,3);wPb=new OPb("ISTEMPLATIZED",6,"isTemplatized",1,1,3);xPb=new OPb("ISUNKNOWN",7,"isUnknown",1,1,3);sPb=new OPb(uSc,8,"instanceOf",1,1,1);zPb=new OPb("MAPUNION",9,"mapunion",2,2,1);yPb=new OPb("MAPRECORD",10,"maprecord",2,2,1);APb=new OPb(qOc,11,"none",0,0,0);BPb=new OPb("PRINTTYPE",12,"printType",2,2,1);CPb=new OPb("PROPTYPE",13,"propType",2,2,1);DPb=new OPb("RAWTYPEOF",14,"rawTypeOf", +1,1,0);GPb=new OPb("SUB",15,"sub",2,2,3);FPb=new OPb("STREQ",16,"streq",2,2,2);EPb=new OPb("RECORD",17,"record",1,_Kc,0);HPb=new OPb("TEMPLATETYPEOF",18,"templateTypeOf",2,2,0);IPb=new OPb(ERc,19,$Nc,2,_Kc,0);JPb=new OPb("TYPEEXPR",20,"typeExpr",1,1,0);KPb=new OPb("TYPEOFVAR",21,"typeOfVar",1,1,1);LPb=new OPb("UNION",22,"union",2,_Kc,0);MPb=new OPb("UNKNOWN",23,vSc,0,0,0)}function f2(){f2=JZ;new Mcb("JSC_INVALID_CLOSURE_IMPORT_DESTRUCTURING",(E2(),B2),new Rtc("Destructuring {0} must be a simple object pattern.")); +new Mcb("JSC_ONE_CLOSURE_IMPORT_PER_DECLARATION",B2,new Rtc("There may only be one {0} per var/let/const declaration."));new Mcb("JSC_INVALID_CLOSURE_IMPORT_CALL",B2,new Rtc("{0} parameter must be a string literal."));new Mcb(hNc,B2,new Rtc(iNc));e2=new Mcb("JSC_LET_CLOSURE_IMPORT",C2,new Rtc(jNc));new Mcb("JSC_NO_CLOSURE_IMPORT_DESTRUCTURING",B2,new Rtc("Cannot destructure the return value of {0}"));new Mcb("JSC_LHS_OF_CLOUSRE_IMPORT_MUST_BE_CONST_IN_ES_MODULE",B2,new Rtc("The left side of a {0} must use ''const'' (not ''let'' or ''var'') in an ES module.")); +x3b((ae(Hsc(kNc,_sc(46))==-1,HMc,kNc),n8b(),new Wbc((Afc(),rec),kNc)),new Wbc(bfc,lNc));x3b(x3b((ae(Hsc(kNc,_sc(46))==-1,HMc,kNc),new Wbc(rec,kNc)),new Wbc(bfc,mNc)),new Wbc(bfc,nNc));x3b((ae(Hsc(kNc,_sc(46))==-1,HMc,kNc),new Wbc(rec,kNc)),new Wbc(bfc,oNc));x3b((ae(Hsc(kNc,_sc(46))==-1,HMc,kNc),new Wbc(rec,kNc)),new Wbc(bfc,pNc))}function J4(a,b,c,d,e,f,g){var h,i,j;j=new ttc;for(i=0;i=2&&(b.charCodeAt(i-1)==45&&b.charCodeAt(i-2)==45||b.charCodeAt(i-1)==93&&b.charCodeAt(i- +2)==93)?(j.a+="\\x3e",j):(j.a+=String.fromCharCode(h),j);break;case 60:if(!a.k&&!g){j.a+="\\x3c";break}Msc(b,true,i+1,"/script",0,7)?(j.a+="\\x3c",j):Msc(b,false,i+1,"!--",0,3)?(j.a+="\\x3c",j):(j.a+=String.fromCharCode(h),j);break;default:if(g){if(h==8232){j.a+="\\u2028";break}if(h==8233){j.a+="\\u2029";break}}h>31&&h<127?(j.a+=String.fromCharCode(h),j):wD(j,h)}}return j.a}function RKb(a){RJb();switch(a.f){case 74:return Afc(),Tcc;case 75:return Afc(),Ucc;case 73:return Afc(),Rcc;case 59:return Afc(), +xdc;case 60:return Afc(),vec;case 54:return Afc(),lec;case 56:return Afc(),iec;case 55:return Afc(),Rdc;case 57:return Afc(),Ldc;case 70:return Afc(),kec;case 71:return Afc(),Wec;case 72:return Afc(),tfc;case 63:return Afc(),vcc;case 64:return Afc(),efc;case 65:return Afc(),qec;case 94:return Afc(),pdc;case 67:return Afc(),oec;case 66:return Afc(),zdc;case 61:return Afc(),Zec;case 62:return Afc(),$ec;case 16:return Afc(),Zdc;case 17:return Afc(),aec;case 53:return Afc(),edc;case 82:return Afc(),Ccc; +case 92:return Afc(),Fcc;case 93:return Afc(),Gcc;case 91:return Afc(),Ecc;case 88:return Afc(),Jcc;case 89:return Afc(),Mcc;case 90:return Afc(),Occ;case 83:return Afc(),Dcc;case 84:return Afc(),Ncc;case 85:return Afc(),Lcc;case 86:return Afc(),Icc;case 95:return Afc(),Hcc;case 87:return Afc(),Kcc;case 79:return Afc(),Hec;case 78:return Afc(),wcc;default:throw gZ(new Irc(!a?jLc:a.a==null?(OSb(),Po(pSb,a)).c:a.a));}}function dXb(a){var b,c,d,e,f;yWb(a);if(!xWb(a,a.f)){FWb(a,"Unterminated template literal escape sequence", +CE(xE(iW,1),mLc,1,5,[]));return null}if(jXb(xWb(a,a.f)?zsc(a.b,a.f):0)){d=yWb(a);d==13&&(xWb(a,a.f)?zsc(a.b,a.f):0)==10&&yWb(a);return null}e=yWb(a);switch(e){case 48:if(qXb(xWb(a,a.f)?zsc(a.b,a.f):0)>=0)return new rXb(_Sc,wWb(a,a.d.a.length==0?a.f:DWb(a,0).d.b.c));return null;case 49:case 50:case 51:case 52:case 53:case 54:case 55:return new rXb(_Sc,wWb(a,a.d.a.length==0?a.f:DWb(a,0).d.b.c));case 120:c=SWb(a)&&SWb(a);if(!c)return new rXb(bTc,wWb(a,a.d.a.length==0?a.f:DWb(a,0).d.b.c));return null; +case 117:if((xWb(a,a.f)?zsc(a.b,a.f):0)==123){yWb(a);if((xWb(a,a.f)?zsc(a.b,a.f):0)==125)return new rXb(cTc,wWb(a,a.d.a.length==0?a.f:DWb(a,0).d.b.c));b=true;while((xWb(a,a.f)?zsc(a.b,a.f):0)!=125&&b)b=b&&SWb(a);if(!b)return new rXb(bTc,wWb(a,a.d.a.length==0?a.f:DWb(a,0).d.b.c));yWb(a);return null}else{f=SWb(a)&&SWb(a)&&SWb(a)&&SWb(a);if(!f)return new rXb(bTc,wWb(a,a.d.a.length==0?a.f:DWb(a,0).d.b.c));return null}default:return null}}function Pub(a,b,c,d){var e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v;f= +a.c++;h="$jscomp$forAwait$tempIterator"+f;r="$jscomp$forAwait$tempResult"+f;Yd(!!d.g,"Cannot replace parentless for-await-of");j=g9b(d);s=g9b(d);m=g9b(d);t=(new bvb(a,s),null);g=I9b(d_(h,R$((i=s,Kob(b),i))),s);Vjb(false,a.d,22,t);q=d_(r,(Sd(c.a),u=(ae(Hsc(h,_sc(46))==-1,HMc,h),n8b(),new Wbc((Afc(),rec),h)),S8b(c.a)?v=f_(M3b(b_((Kob(b),NQc)),CE(xE(HU,1),IMc,7,0,[b_((Kob(b),SQc)),Tqb(x3b(u,new Wbc(bfc,wPc)),CE(xE(HU,1),IMc,7,0,[]))]))):v=K$(Tqb(x3b(u,new Wbc(bfc,wPc)),CE(xE(HU,1),IMc,7,0,[]))),v)); +e=B3b(O$((o=(ae(Hsc(r,_sc(46))==-1,HMc,r),new Wbc(rec,r)),o),"done"),k3b(CE(xE(HU,1),IMc,7,0,[new J9b(Xcc)])));if(X8b(j))k=h_((p=h3b(j,O$((n=(ae(Hsc(r,_sc(46))==-1,HMc,r),new Wbc(rec,r)),n),yPc)),p));else{ppb();if(!!j&&(j.n==ufc||j.n==jec||j.n==gdc)){q8b(j.c,O$((n=(ae(Hsc(r,_sc(46))==-1,HMc,r),new Wbc(rec,r)),n),yPc));k=j}else throw gZ(new Kqc("unexpected for-await-of lhs"));}I9b(k,j);l=u3b(g,new J9b(tdc),new J9b(tdc),k3b(CE(xE(HU,1),IMc,7,0,[q,e,k,m.n==Vcc?m:F9b(k3b(CE(xE(HU,1),IMc,7,0,[m])),m)]))); +j9b(d.g,d,l);I9b(l,d);w7(a.a,l)}function arb(a){ppb();switch(a.f){case 46:return 0;case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 59:case 57:case 58:case 47:return 1;case 103:return 2;case 60:return 3;case 61:return 4;case 62:return 5;case 1:return 6;case 2:return 7;case 3:return 8;case 4:case 5:case 36:case 37:return 9;case 6:case 8:case 7:case 9:case 41:case 40:return 10;case 10:case 11:case 12:return 11;case 14:case 13:return 12;case 15:case 17:case 16:return 13;case 18:return 14; +case 104:case 23:case 24:case 25:case 79:case 19:case 20:case 21:case 22:return 15;case 63:case 64:return 16;case 28:case 27:case 26:case 122:case 42:case 93:case 121:case 95:case 80:case 34:case 65:case 96:case 151:case 29:case 32:case 30:case 43:case 94:case 38:case 114:case 115:case 31:case 91:case 159:case 160:case 161:case 33:case 99:case 35:case 117:case 118:case 113:case 128:return 17;case 126:return 18;case 137:case 127:return 19;case 123:case 125:case 124:case 129:case 135:case 130:case 133:case 136:case 138:return 20; +case 92:return 21;default:Kd(a!=(Afc(),jfc));throw gZ(new Irc("Unknown precedence for "+a));}}function Upc(a,b){var c,d,e,f,g,h,i,j,k,l,m,n,o,p;if(b.ti())return a;a.e=a.e||b.mi();a.c=a.c||b.Ci();g=pF(b,212);a.f=a.f||g;g&&(a.b=a.b&&b.ni());if(!a.e&&!a.f)if(b.Mi()){o=b.Mi();c=(Yoc(new Epc,o.b)&&Voc(o),o.b);for(f=0;fa.g)return a;if(!!b.Ii()&&a.d!=-1){i=$wc(a.a,a.d).Ii();l=Jjc(b.Ii(),i,true);cxc(a.a,a.d,l);a.j=null;return a}e=0;h=new wxc(a.a);while(h.a< +h.c.a.length){k=false;d=(h.b=h.a++,h.c.a[h.b]);if(b.Bi()||d.Bi()||b.si()||d.si()||Wfc(b)||Wfc(d)){if($fc(b,d,a.k))return a}else if(!!b.Li()||!!d.Li())if(d.Li())if(b.Li()){Xd(!!d.Li()&&!!b.Li());m=b.Li();n=d.Li();if(Zfc(n.e,m.e))if(Onc(b.ki(),d.ki(),0,new ukc(true),0))return a;else{j=n.d;b=new koc(a.i,j,(Bn(),Bn(),An));k=true}}else Ypc(a,d,b)&&(k=true);else{if(Ypc(a,b,d))return a}else if(Ypc(a,b,d)){!!b.Ji()&&!!d.Ji()&&glc(a.i,b.Ji(),d.Ji());return a}else if(Ypc(a,d,b)){!!d.Ji()&&!!b.Ji()&&glc(a.i, +d.Ji(),b.Ji());k=true}if(k){axc(h.c,h.a=h.b);h.b=-1;if(e==a.d)a.d=-1;else if(e=0||FWb(a,"Binary Integer Literal must contain at least one digit",CE(xE(iW,1),mLc,1,5,[]));OWb(a);return new dTb((AZb(),DYb),Vsc(a.b,b,a.f),$Sb(a.i.b,b,a.f));case 111:case 79:yWb(a);qXb(xWb(a,a.f)?zsc(a.b,a.f):0)>= +0||FWb(a,"Octal Integer Literal must contain at least one digit",CE(xE(iW,1),mLc,1,5,[]));VWb(a);((xWb(a,a.f)?zsc(a.b,a.f):0)==56||(xWb(a,a.f)?zsc(a.b,a.f):0)==57)&&FWb(a,ORc,CE(xE(iW,1),mLc,1,5,[]));return new dTb((AZb(),DYb),Vsc(a.b,b,a.f),$Sb(a.i.b,b,a.f));case 120:case 88:yWb(a);Uqc(xWb(a,a.f)?zsc(a.b,a.f):0,16)>=0||FWb(a,"Hex Integer Literal must contain at least one digit",CE(xE(iW,1),mLc,1,5,[]));TWb(a);return new dTb((AZb(),DYb),Vsc(a.b,b,a.f),$Sb(a.i.b,b,a.f));case 101:case 69:return HWb(a, +b);case 46:return IWb(a,b);case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:RWb(a);if((xWb(a,a.f)?zsc(a.b,a.f):0)==46){yWb(a);RWb(a)}return new dTb((AZb(),DYb),Vsc(a.b,b,a.f),$Sb(a.i.b,b,a.f));default:return new dTb((AZb(),DYb),Vsc(a.b,b,a.f),$Sb(a.i.b,b,a.f))}}function Cpb(a,b){var c,d,e,f;Yd(a.n==(Afc(),Ycc)||a.n==hfc,a);if(Q9b(E8b(a,(Hbc(),wbc)),15))return false;if(Q9b(E8b(a,wbc)&15,11)&&rpb(a))return false;c=a.c;if(c.n==rec){e=c.Wh();if(gpb.Sd(e))return false}else if(c.n== +Pdc){if(!!a.c&&!a.c.f&&mpb.Sd((c.c?c.c.i:null).Wh()))return false;if(Q9b(E8b(a,wbc)&15,13)&&Apb(c.c,(se(),oe)))return false;if(c.c.n==rec&&V8b(c)&&Esc(c.c.Wh(),$Pc))switch((c.c?c.c.i:null).Wh()){case "abs":case "acos":case "acosh":case "asin":case "asinh":case "atan":case "atanh":case "atan2":case "cbrt":case "ceil":case "cos":case "cosh":case "exp":case "expm1":case "floor":case "hypot":case "log":case "log10":case "log1p":case "log2":case "max":case "min":case "pow":case "round":case "sign":case "sin":case "sinh":case "sqrt":case "tan":case "tanh":case "trunc":return false; +case "random":return!(!!a.c&&!a.c.f)}if(!!b&&!b.ng())if(c.c.n==Rec&&npb.Sd((c.c?c.c.i:null).Wh()))return false;else if(Lqb(c.c,b)){d=(c.c?c.c.i:null).Wh();f=c.f;if(f)if(f.n==bfc){if(opb.Sd(d))return false}else if(f.n==Rec)if(Esc("replace",d))return f.f.n!=bfc;else if(opb.Sd(d))return false}}return true}function v6(){v6=JZ;RZ();t6=new Mcb("JSC_MODULE_DEPENDENCY_ERROR",(E2(),B2),new Rtc("Bad dependency: {0} -> {1}. Modules must be listed in dependency order."));r6=new Mcb("JSC_MISSING_ENTRY_ERROR", +B2,new Rtc('required entry point "{0}" never provided'));s6=new Mcb("JSC_MISSING_MODULE_ERROR",B2,new Rtc('unknown module "{0}" specified in entry point spec'));new Mcb("JSC_INCONSISTENT_MODULE_DEFINITIONS",B2,new Rtc("Serialized module definitions are not consistent with the module definitions supplied in the command line"));new Mcb("JSC_OPTIMIZE_LOOP_ERROR",B2,new Rtc("Exceeded max number of optimization iterations: {0}"));new Mcb("JSC_MOTION_ITERATIONS_ERROR",B2,new Rtc("Exceeded max number of code motion iterations: {0}")); +u6=RIc("com.google.javascript.jscomp");new Pc(tqc);p6=new Mcb("JSC_EMPTY_MODULE_LIST_ERROR",B2,new Rtc("At least one module must be provided"));q6=new Mcb("JSC_EMPTY_ROOT_MODULE_ERROR",B2,new Rtc("Root module ''{0}'' must contain at least one source code input"));o6=new Mcb("JSC_DUPLICATE_INPUT",B2,new Rtc("Duplicate input: {0}"));n6=new Mcb("JSC_DUPLICATE_EXTERN_INPUT",B2,new Rtc("Duplicate extern input: {0}"))}function H3(){H3=JZ;new Mcb("JSC_INVALID_DESTRUCTURING_FORWARD_DECLARE",(E2(),B2),new Rtc("Cannot destructure a forward-declared type")); +new Mcb("JSC_MODULE_USES_GOOG_MODULE_GET",B2,new Rtc("It's illegal to use a 'goog.module.get' at the module top-level. Did you mean to use goog.require instead?"));new Mcb("JSC_GOOG_MODULE_INVALID_FORWARD_DECLARE_NAMESPACE",B2,new Rtc("goog.forwardDeclare parameter must be a string literal."));new Mcb("JSC_GOOG_MODULE_INVALID_GET_NAMESPACE",B2,new Rtc("goog.module.get parameter must be a string literal."));new Mcb("JSC_GOOG_MODULE_INVALID_REQUIRE_NAMESPACE",B2,new Rtc("goog.require parameter must be a string literal.")); +new Mcb("JSC_GOOG_MODULE_INVALID_REQUIRE_TYPE_NAMESPACE",B2,new Rtc("goog.requireType parameter must be a string literal."));G3=new Mcb("JSC_MISSING_MODULE_OR_PROVIDE",B2,new Rtc('Required namespace "{0}" never defined.'));new Mcb("JSC_GOOG_MODULE_INVALID_GET_CALL_SCOPE",B2,new Rtc("goog.module.get can not be called in global scope."));F3=new Mcb("JSC_INVALID_CLOSURE_CALL_ERROR",B2,new Rtc("Closure primitive methods (goog.provide, goog.require, goog.define, etc) must be called at file scope."))} +function Vtb(){Vtb=JZ;var a,b,c,d;new Cc(new Cc(new jc(97,122),new jc(65,90)),new gc);Utb=new Mcb("JSC_UNKNOWN_TWEAK_WARNING",(E2(),D2),new Rtc("no tweak registered with ID {0}"));new Mcb("JSC_TWEAK_MULTIPLY_REGISTERED_ERROR",B2,new Rtc("Tweak {0} has already been registered."));new Mcb("JSC_NON_LITERAL_TWEAK_ID_ERROR",B2,new Rtc("tweak ID must be a string literal"));Rtb=new Mcb("JSC_INVALID_TWEAK_DEFAULT_VALUE_WARNING",D2,new Rtc("tweak {0} registered with {1} must have a default value that is a literal of type {2}")); +new Mcb("JSC_NON_GLOBAL_TWEAK_INIT_ERROR",B2,new Rtc("tweak declaration {0} must occur in the global scope"));new Mcb("JSC_TWEAK_OVERRIDE_AFTER_REGISTERED_ERROR",B2,new Rtc("Cannot override the default value of tweak {0} after it has been registered"));Ttb=new Mcb("JSC_TWEAK_WRONG_GETTER_TYPE_WARNING",D2,new Rtc("tweak getter function {0} used for tweak registered using {1}"));new Mcb("JSC_INVALID_TWEAK_ID_ERROR",B2,new Rtc("tweak ID contains illegal characters. Only letters, numbers, _ and . are allowed")); +Stb=new fDc;for(b=(cub(),CE(xE(JO,1),sLc,90,0,[_tb,aub,bub,$tb,Xtb,Wtb,Ytb,Ztb])),c=0,d=b.length;c1&&VVb(a,"for-in statement may not have more than one variable declaration",CE(xE(iW,1),mLc,1,5,[]));c=f.b.ce(0);!!c.b&&(a.b.a?VVb(a,"for-in statement may not have initializer",CE(xE(iW,1),mLc,1,5,[])):EOb(a.c,c.o.b,"for-in statement should not have initializer",CE(xE(iW,1),mLc,1,5,[])));return lUb(a, +e,f)}else if(zVb(a,0,iYb)&&Esc(DWb(a.g,0).a,"of")){f.b.Yd()>1&&(b?VVb(a,"for-await-of statement may not have more than one variable declaration",CE(xE(iW,1),mLc,1,5,[])):VVb(a,"for-of statement may not have more than one variable declaration",CE(xE(iW,1),mLc,1,5,[])));c=f.b.ce(0);!!c.b&&(b?VVb(a,"for-await-of statement may not have initializer",CE(xE(iW,1),mLc,1,5,[])):VVb(a,"for-of statement may not have initializer",CE(xE(iW,1),mLc,1,5,[])));return b?kUb(a,e,f):mUb(a,e,f)}else{gTb(a,f);return oUb(a, +e,f)}}if(AVb(a,ZYb))return oUb(a,e,null);d=xTb(a,0);if(AVb(a,mYb)||AVb(a,ZXb)||zVb(a,0,iYb)&&Esc(DWb(a.g,0).a,"of")){d=WVb(a,d);HZb(d)||VVb(a,WSc,CE(xE(iW,1),mLc,1,5,[]))}if(AVb(a,mYb)||zVb(a,0,iYb)&&Esc(DWb(a.g,0).a,"of"))if(d.p!=(n1b(),F_b)&&d.p!=N_b)return AVb(a,mYb)?lUb(a,e,d):b?kUb(a,e,d):mUb(a,e,d);return oUb(a,e,d)}function kwb(){kwb=JZ;bwb=new Mcb("JSC_PARSE_ERROR",(E2(),B2),new Rtc(XQc));gwb=new Mcb("JSC_TYPE_PARSE_ERROR",D2,new Rtc(GOc));iwb=new Mcb("JSC_UNRECOGNIZED_TYPE_ERROR",D2,new Rtc(GOc)); +Zvb=new Mcb("JSC_JSDOC_MISSING_BRACES_WARNING",C2,new Rtc(GOc));$vb=new Mcb("JSC_JSDOC_MISSING_TYPE_WARNING",C2,new Rtc(GOc));ewb=new Mcb("JSC_TOO_MANY_TEMPLATE_PARAMS",C2,new Rtc(GOc));fwb=new Mcb("JSC_TRAILING_COMMA",B2,new Rtc("Parse error. IE8 (and below) will parse trailing commas in array and object literals incorrectly. If you are targeting newer versions of JS, set the appropriate language_in option."));Uvb=new Mcb("JSC_DUPLICATE_PARAM",B2,new Rtc(XQc));hwb=new Mcb("JSC_UNNECESSARY_ESCAPE", +C2,new Rtc(XQc));Yvb=new Mcb("JSC_INVALID_PARAM",D2,new Rtc(XQc));Tvb=new Mcb("JSC_BAD_JSDOC_ANNOTATION",D2,new Rtc(XQc));Wvb=new Mcb("JSC_INVALID_ES3_PROP_NAME",D2,new Rtc(YQc));cwb=new Mcb("JSC_PARSE_TREE_TOO_DEEP",B2,new Rtc("Parse tree too deep."));Xvb=new Mcb("JSC_INVALID_OCTAL_LITERAL",D2,new Rtc("This style of octal literal is not supported in strict mode."));dwb=new Mcb("JSC_STRING_CONTINUATION",D2,new Rtc(GOc));_vb=new Mcb("JSC_LANGUAGE_FEATURE",B2,new Rtc("{0}."));jwb=new Mcb("JSC_UNSUPPORTED_LANGUAGE_FEATURE", +B2,new Rtc("{0}."));Vvb=new Mcb("JSC_ES6_TYPED",B2,new Rtc("{0}. Use --language_in=ECMASCRIPT6_TYPED to enable ES6 typed features."));awb=new Mcb("JSC_MISPLACED_TYPE_SYNTAX",B2,new Rtc(ZQc))}function C5b(a,b){var c,d,e,f,g,h,i,j;if(!a&&!b)return true;if(!a||!b)return false;if(!kFc(G4b(a),G4b(b)))return false;for(h=G4b(a).Vd();h.Id();){g=h.Jd();if(!kFc(H4b(a,g),H4b(b,g)))return false}if((!a.b||!a.b.d?(Bn(),Bn(),An):a.b.d).Yd()!=(!b.b||!b.b.d?(Bn(),Bn(),An):b.b.d).Yd())return false;c=(!a.b||!a.b.d? +(Bn(),Bn(),An):a.b.d).Vd();d=(!b.b||!b.b.d?(Bn(),Bn(),An):b.b.d).Vd();while(c.Id())if(!R5b(c.Jd(),d.Jd()))return false;return kFc(!a.b?null:a.b.a,!b.b?null:b.b.a)&&kFc(!a.d?null:a.d.a,!b.d?null:b.d.a)&&(!a.b?null:a.b.b)==(!b.b?null:b.b.b)&&(!a.b?null:a.b.c)==(!b.b?null:b.b.c)&&kFc(C4b(a),C4b(b))&&kFc((HPc&a.a)==eQc?a.i:null,(HPc&b.a)==eQc?b.i:null)&&kFc(A4b(a),A4b(b))&&kFc(!a.d?null:a.d.i,!b.d?null:b.d.i)&&(!a.d?null:a.d.j)==(!b.d?null:b.d.j)&&(!a.d?null:a.d.k)==(!b.d?null:b.d.k)&&kFc((f=!a.d?null: +a.d.n,!f?(Cyc(),Cyc(),Byc):f),(e=!b.d?null:b.d.n,!e?(Cyc(),Cyc(),Byc):e))&&(!a.b?null:a.b.i)==(!b.b?null:b.b.i)&&(!a.d?0:a.d.p)==(!b.d?0:b.d.p)&&kFc(!a.b?null:a.b.g,!b.b?null:b.b.g)&&(!a.b?null:a.b.f)==(!b.b?null:b.b.f)&&kFc((HPc&a.a)==$Lc?a.i:null,(HPc&b.a)==$Lc?b.i:null)&&kFc((j=!a.d?null:a.d.q,!j?(Cyc(),Cyc(),Byc):j),(i=!b.d?null:b.d.q,!i?(Cyc(),Cyc(),Byc):i))&&kFc(J4b(a),J4b(b))&&kFc(a.g,b.g)&&kFc(K4b(a),K4b(b))&&kFc((HPc&a.a)==hMc?a.i:null,(HPc&b.a)==hMc?b.i:null)&&kFc((HPc&a.a)==WPc?a.i:null, +(HPc&b.a)==WPc?b.i:null)&&(!a.b?null:a.b.k)==(!b.b?null:b.b.k)&&kFc(a.j,b.j)&&a.a==b.a}function jHb(){jHb=JZ;ZGb=new Mcb("JSC_CLASS_DISALLOWED_JSDOC",(E2(),C2),new Rtc("@constructor annotations are redundant on classes."));aHb=new Mcb("JSC_MISSING_JSDOC",C2,new Rtc("Function must have JSDoc."));bHb=new Mcb("JSC_MISSING_PARAMETER_JSDOC",C2,new Rtc("Parameter must have JSDoc."));dHb=new Mcb("JSC_MIXED_PARAM_JSDOC_STYLES",C2,new Rtc("Functions may not use both @param annotations and inline JSDoc")); +cHb=new Mcb("JSC_MISSING_RETURN_JSDOC",C2,new Rtc("Function with non-trivial return must have JSDoc indicating the return type."));eHb=new Mcb("JSC_MUST_BE_PRIVATE",C2,new Rtc("Property {0} must be marked @private"));fHb=new Mcb("JSC_MUST_HAVE_TRAILING_UNDERSCORE",C2,new Rtc("Private property {0} should end with ''_''"));gHb=new Mcb("JSC_OPTIONAL_PARAM_NOT_MARKED_OPTIONAL",C2,new Rtc("Parameter {0} is optional so its type must end with ="));iHb=new Mcb("JSC_WRONG_NUMBER_OF_PARAMS",C2,new Rtc("Wrong number of @param annotations")); +_Gb=new Mcb("JSC_INCORRECT_PARAM_NAME",C2,new Rtc("Incorrect param name. Are your @param annotations in the wrong order?"));$Gb=new Mcb("JSC_EXTERNS_FILES_SHOULD_BE_ANNOTATED",C2,new Rtc("Externs files should be annotated with @externs in the @fileoverview block."));hHb=new Mcb("JSC_PREFER_BACKTICKS_TO_AT_SIGN_CODE",C2,new Rtc("Use `some_code` instead of '{'@code some_code'}'."));YGb=new bcb(CE(xE(bM,1),mLc,5,0,[ZGb,aHb,bHb,dHb,cHb,eHb,fHb,gHb,iHb,_Gb,$Gb,hHb]))}function oCb(){oCb=JZ;gCb=(Ofc(),"(Proxy)"); +mCb=new Mcb("JSC_MALFORMED_TYPEDEF",(E2(),D2),new Rtc("Typedef for {0} does not have any type information"));iCb=new Mcb("JSC_ENUM_INITIALIZER_NOT_ENUM",D2,new Rtc("enum initializer must be an object literal or an enum"));new Mcb("JSC_INVALID_ENUM_KEY",D2,new Rtc("enum key must be a string or numeric literal"));fCb=new Mcb("JSC_CTOR_INITIALIZER_NOT_CTOR",D2,new Rtc("Constructor {0} must be initialized at declaration"));jCb=new Mcb("JSC_IFACE_INITIALIZER_NOT_IFACE",D2,new Rtc("Interface {0} must be initialized at declaration")); +eCb=new Mcb("JSC_REFLECT_CONSTRUCTOR_EXPECTED",D2,new Rtc("Constructor expected as first argument"));nCb=new Mcb("JSC_UNKNOWN_LENDS",D2,new Rtc("Variable {0} not declared before @lends annotation."));lCb=new Mcb("JSC_LENDS_ON_NON_OBJECT",D2,new Rtc("May only lend properties to object types. {0} has type {1}."));kCb=new Mcb("JSC_INCOMPATIBLE_ALIAS_ANNOTATION",D2,new Rtc("Annotation {0} on {1} incompatible with aliased type."));hCb=new Mcb("JSC_DYNAMIC_EXTENDS_WITHOUT_JSDOC",D2,new Rtc("The right-hand side of an extends clause must be a qualified name, or else @extends must be specified in JSDoc")); +new Mcb("JSC_CONFLICTING_GETTER_SETTER_TYPE",D2,new Rtc("The types of the getter and setter for property ''{0}'' do not match.\ngetter type is: {1}\nsetter type is: {2}"));new _bb(gCb,CE(xE(bM,1),mLc,5,0,[mCb,iCb,fCb,jCb,eCb,nCb,lCb,kCb,hCb]))}function MBb(){MBb=JZ;HBb=new Mcb("JSC_INVALID_CAST",(E2(),D2),new Rtc("invalid cast - must be a subtype or supertype\nfrom: {0}\nto : {1}"));KBb=new Mcb("JSC_TYPE_MISMATCH",D2,new Rtc(GOc));GBb=new Mcb("JSC_INVALID_ASYNC_RETURN_TYPE",D2,new Rtc("The return type of an async function must be a supertype of Promise\nfound: {0}")); +IBb=new Mcb("JSC_INVALID_OPERAND_TYPE",C2,new Rtc(GOc));JBb=new Mcb("JSC_MISSING_EXTENDS_TAG",D2,new Rtc("Missing @extends tag on type {0}"));BBb=new Mcb("JSC_DUP_VAR_DECLARATION",D2,new Rtc("variable {0} redefined, original definition at {1}:{2}"));CBb=new Mcb("JSC_DUP_VAR_DECLARATION_TYPE_MISMATCH",D2,new Rtc("variable {0} redefined with type {1}, original definition at {2}:{3} with type {4}"));FBb=new Mcb("JSC_INTERFACE_METHOD_NOT_IMPLEMENTED",D2,new Rtc("property {0} on interface {1} is not implemented by type {2}")); +DBb=new Mcb("JSC_HIDDEN_INTERFACE_PROPERTY_MISMATCH",D2,new Rtc("mismatch of the {0} property on type {1} and the type of the property it overrides from interface {2}\noriginal: {3}\noverride: {4}"));zBb=new Mcb("JSC_ABSTRACT_METHOD_NOT_IMPLEMENTED",D2,new Rtc("property {0} on abstract class {1} is not implemented by type {2}"));LBb=new Mcb("JSC_UNKNOWN_TYPEOF_VALUE",D2,new Rtc("unknown type: {0}"));EBb=new Mcb("JSC_ILLEGAL_PROPERTY_ACCESS",D2,new Rtc("Cannot do {0} access on a {1}"));ABb=new bcb(CE(xE(bM, +1),mLc,5,0,[zBb,BBb,CBb,DBb,EBb,FBb,GBb,HBb,JBb,KBb,LBb]))}function jeb(a,b,c){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,A,B,C,D,F,G,H;H=g9b(b);r=g9b(b);e=g9b(b);F=a.g;if(a.a){s=bgc(r.d.ei());!!s&&(F=Ufc(s,a.e.u[24]))}v=Vjb(a.a,a.e,26,F);t=a.a?(D=Chc(v?v:null,wPc),!D?null:D.Ni((v?v:null).b)).Ii():null;l=a.a?t.b.b:null;G=H8b(H,(Hbc(),mbc));p=Yjb(K3b("$jscomp$iter$"+(""+(new j8(a.c)).a.Q++)),v);c9b(p,pbc,1);j=Yjb(l3b(Yjb(x3b(x8b(p,false),leb(a,(n8b(),new Wbc((Afc(),bfc),wPc)))),t),CE(xE(HU,1),IMc, +7,0,[])),l);u="$jscomp$key$";ppb();!!H&&(H.n==ufc||H.n==jec||H.n==gdc)?u+=H.c.Wh():H.n==rec?u+=H.Wh():u+=Hbb(a.d);q=Yjb((ae(Hsc(u,_sc(46))==-1,HMc,u),new Wbc(rec,u)),l);c9b(q,pbc,1);f=Xjb(a.c,r);if(a.a){C=Upc(Upc(Upc(new $pc(a.e,false),a.e.u[45]),a.e.u[26]),a.e.u[24]);d=blc(a.e,"Arguments");!!d&&Upc(C,d);w=Nkc(a.e,v,CE(xE(cV,1),xPc,23,0,[Vpc(C)]));k=f.c;k.d=w;r9b(k.c,a.e.u[51]);r9b(k.c.f,a.e.u[45]);f.d=v}n=q3b(Yjb(x8b(p,false),p.d),f,ufc);o=x8b(q,false);r8b(o,x8b(j,false));q8b(n,o);g=keb(a,N3b(keb(a, +x3b(x8b(q,false),leb(a,new Wbc(bfc,"done"))))));m=Yjb(h3b(x8b(q,false),x8b(j,false)),l);if(!!H&&(H.n==ufc||H.n==jec||H.n==gdc)){i=H.n;h=r9b(new L9b(i,F9b(K3b(H.c.Wh()),H.c)),F);q8b(h.c,Yjb(x3b(x8b(q,false),leb(a,new Wbc(bfc,yPc))),F));d9b(h,mbc,G)}else{h=Yjb(h3b(Yjb(q9b(x8b(H,false),null),F),Yjb(x3b(x8b(q,false),leb(a,new Wbc(bfc,yPc))),F)),F);d9b(h,mbc,G);h=(Yd(H3b(h),h),new L9b(Ddc,h))}A=F9b(k3b(CE(xE(HU,1),IMc,7,0,[h,e])),e);B=u3b(n,g,m,A);I9b(B,b);j9b(c,b,B);w7(a.c,B)}function A2(){A2=JZ;var a; +y2=new Mcb("JSC_MISPLACED_MSG_ANNOTATION",(E2(),C2),new Rtc("Misplaced message annotation. @desc, @hidden, and @meaning annotations should only be on message nodes.\nMessage constants must be prefixed with 'MSG_'."));x2=new Mcb("JSC_MISPLACED_ANNOTATION",D2,new Rtc("Misplaced {0} annotation. {1}"));q2=new Mcb("JSC_ANNOTATION_DEPRECATED",D2,new Rtc("The {0} annotation is deprecated. {1}"));t2=new Mcb("JSC_DISALLOWED_MEMBER_JSDOC",D2,new Rtc("Class level JSDocs (@interface, @extends, etc.) are not allowed on class members")); +r2=new Mcb("JSC_ARROW_FUNCTION_AS_CONSTRUCTOR",B2,new Rtc("Arrow functions cannot be used as constructors"));s2=new Mcb("JSC_DEFAULT_PARAM_MUST_BE_MARKED_OPTIONAL",B2,new Rtc("Inline JSDoc on default parameters must be marked as optional"));v2=new Mcb("JSC_INVALID_NO_SIDE_EFFECT_ANNOTATION",B2,new Rtc("@nosideeffects may only appear in externs files."));u2=new Mcb("JSC_INVALID_MODIFIES_ANNOTATION",B2,new Rtc("@modifies may only appear in externs files."));new Mcb("JSC_INVALID_DEFINE_ON_LET",B2,new Rtc("variables annotated with @define may only be declared with VARs, ASSIGNs, or CONSTs")); +z2=new Mcb("JSC_MISPLACED_SUPPRESS",D2,new Rtc("@suppress annotation not allowed here. See https://github.com/google/closure-compiler/wiki/@suppress-annotations"));w2=new Mcb("JSC_JSDOC_IN_BLOCK_COMMENT",D2,new Rtc("Non-JSDoc comment has annotations. Did you mean to start it with '/**'?"));a=new XIc;a.a=new $wnd.RegExp("(/|(\n[ \t]*))\\*[ \t]*@[a-zA-Z]+[ \t\n{]")}function Pxb(){Pxb=JZ;Oxb=new Mcb("JSC_USE_OF_WITH",(E2(),D2),new Rtc("The 'with' statement cannot be used in strict mode."));Lxb=new Mcb("JSC_EVAL_DECLARATION", +D2,new Rtc('"eval" cannot be redeclared in strict mode'));Kxb=new Mcb("JSC_EVAL_ASSIGNMENT",D2,new Rtc('the "eval" object cannot be reassigned in strict mode'));Gxb=new Mcb("JSC_ARGUMENTS_DECLARATION",D2,new Rtc('"arguments" cannot be redeclared in strict mode'));Dxb=new Mcb("JSC_ARGUMENTS_ASSIGNMENT",D2,new Rtc('the "arguments" object cannot be reassigned in strict mode'));Exb=new Mcb("JSC_ARGUMENTS_CALLEE_FORBIDDEN",D2,new Rtc('"arguments.callee" cannot be used in strict mode'));Fxb=new Mcb("JSC_ARGUMENTS_CALLER_FORBIDDEN", +D2,new Rtc('"arguments.caller" cannot be used in strict mode'));Nxb=new Mcb("JSC_FUNCTION_CALLER_FORBIDDEN",D2,new Rtc("A function''s \"caller\" property cannot be used in strict mode"));Mxb=new Mcb("JSC_FUNCTION_ARGUMENTS_PROP_FORBIDDEN",D2,new Rtc("A function''s \"arguments\" property cannot be used in strict mode"));Hxb=new Mcb("JSC_BAD_FUNCTION_DECLARATION",D2,new Rtc("functions can only be declared at top level or immediately within another function in ES5 strict mode"));Ixb=new Mcb("JSC_DELETE_VARIABLE", +D2,new Rtc("variables, functions, and arguments cannot be deleted in strict mode"));Jxb=new Mcb("JSC_DUPLICATE_OBJECT_KEY",B2,new Rtc('Object literal contains illegal duplicate key "{0}", disallowed in strict mode'));new Mcb("JSC_DUPLICATE_CLASS_METHODS",B2,new Rtc('Class contains duplicate method name "{0}"'))}function nKb(a,b,c){var d,e,f,g,h,i,j,k,l;l=b.c;l=Psc(l,"\r\n?",gMc);k=c?0:1;f=Hsc(l,_sc(92));if(f==-1)return c?l:Vsc(l,1,l.length-1);j=new stc;while(f!=-1){j.a+=""+(l==null?jLc:l).substr(k, +f-k);f+=1;d=l.charCodeAt(f);switch(d){case 98:j.a+="\b";break;case 102:j.a+="\f";break;case 110:j.a+=gMc;break;case 114:j.a+="\r";break;case 116:j.a+="\t";break;case 118:j.a+="\x0B";break;case 10:aKb(a,b,(IRb(),ARb));a.d.Bh("String continuations are not recommended. See https://google.github.io/styleguide/jsguide.html#features-strings-no-line-continuations",a.q,b.d.b.b+1,b.d.b.a);break;case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:f+1=0)++g;h=l.substr(f+2,g-(f+2));++g}}e=Crc(h,16);if(e>lMc){a.d.Ah("Undefined Unicode code-point", +a.q,b.d.b.b+1,b.d.b.a);j.a+="\\u{";j.a+=""+h;j.a+="}"}else qtc(j,(EKc(e>=0&&e<=lMc),e>=kMc?CE(xE(xF,1),eLc,46,15,[55296+(e-kMc>>10&mMc)&dLc,56320+(e-kMc&mMc)&dLc]):CE(xE(xF,1),eLc,46,15,[e&dLc])));f=g-1;break;case 39:case 34:case 92:default:j.a+=String.fromCharCode(d)}k=f+1;f=Isc(l,_sc(92),k)}ntc(j,l,k,c?l.length:l.length-1);return j.a}function $kb(){$kb=JZ;Okb=new Mcb("JSC_EXTENDS_WITHOUT_TYPEDEF",(E2(),D2),new Rtc("@extends used without @constructor or @interface for {0}"));Nkb=new Mcb("JSC_EXTENDS_NON_OBJECT", +D2,new Rtc("{0} @extends non-object type {1}"));Skb=new Mcb("JSC_RESOLVED_TAG_EMPTY",D2,new Rtc("Could not resolve type in {0} tag of {1}"));Pkb=new Mcb("JSC_IMPLEMENTS_WITHOUT_CONSTRUCTOR",D2,new Rtc("@implements used without @constructor or @interface for {0}"));Mkb=new Mcb("JSC_CONSTRUCTOR_REQUIRED",D2,new Rtc("{0} used without @constructor for {1}"));Zkb=new Mcb("JSC_VAR_ARGS_MUST_BE_LAST",D2,new Rtc("variable length argument must be last"));Rkb=new Mcb("JSC_OPTIONAL_ARG_AT_END",D2,new Rtc("optional arguments must be at the end")); +Qkb=new Mcb("JSC_INEXISTENT_PARAM",D2,new Rtc("parameter {0} does not appear in {1}''s parameter list"));Ykb=new Mcb("JSC_TYPE_REDEFINITION",D2,new Rtc("attempted re-definition of type {0}\nfound : {1}\nexpected: {2}"));Ukb=new Mcb("JSC_TEMPLATE_TRANSFORMATION_ON_CLASS",D2,new Rtc("Template type transformation {0} not allowed on classes or interfaces"));Vkb=new Mcb("JSC_TEMPLATE_TYPE_DUPLICATED",D2,new Rtc("Only one parameter type must be the template type"));Wkb=new Mcb("JSC_TEMPLATE_TYPE_EXPECTED", +D2,new Rtc("The template type must be a parameter type"));Xkb=new Mcb("JSC_THIS_TYPE_NON_OBJECT",D2,new Rtc("@this type of a function must be an object\nActual type: {0}"));Tkb=new Mcb("JSC_SAME_INTERFACE_MULTIPLE_IMPLEMENTS",D2,new Rtc("Cannot @implement the same interface more than once\nRepeated interface: {0}"));Lkb=new bcb(CE(xE(bM,1),mLc,5,0,[Okb,Nkb,Skb,Pkb,Mkb,Zkb,Rkb,Qkb,Ykb,Ukb,Vkb,Wkb,Xkb,Tkb]))}function OSb(){OSb=JZ;var a,b,c,d,e,f;RRb=new PSb("BREAK",0,"break",(AZb(),DXb));SRb=new PSb("CASE", +1,"case",GXb);TRb=new PSb(FSc,2,"catch",HXb);WRb=new PSb(GSc,3,UNc,QXb);XRb=new PSb(HSc,4,WNc,RXb);ZRb=new PSb(gRc,5,JNc,TXb);$Rb=new PSb("DELETE",6,ISc,UXb);_Rb=new PSb("DO",7,"do",VXb);aSb=new PSb("ELSE",8,"else",WXb);fSb=new PSb(JSc,9,DNc,eYb);gSb=new PSb("FOR",10,"for",fYb);hSb=new PSb(jQc,11,ZKc,gYb);iSb=new PSb("IF",12,"if",jYb);lSb=new PSb("IN",13,"in",mYb);mSb=new PSb(uSc,14,zQc,nYb);tSb=new PSb("NEW",15,"new",yYb);zSb=new PSb(DRc,16,ENc,WYb);CSb=new PSb(KSc,17,"switch",iZb);DSb=new PSb(EMc, +18,FMc,mZb);ESb=new PSb("THROW",19,"throw",nZb);GSb=new PSb("TRY",20,"try",qZb);ISb=new PSb(LSc,21,yQc,sZb);JSb=new PSb("VAR",22,"var",vZb);KSb=new PSb("VOID",23,XNc,wZb);LSb=new PSb("WHILE",24,PNc,xZb);MSb=new PSb("WITH",25,"with",yZb);URb=new PSb(iQc,26,MNc,IXb);VRb=new PSb("CONST",27,XOc,PXb);bSb=new PSb(uRc,28,ZNc,YXb);cSb=new PSb(wRc,29,INc,bYb);dSb=new PSb(vRc,30,NNc,cYb);kSb=new PSb(MSc,31,LNc,lYb);BSb=new PSb(CMc,32,DMc,hZb);jSb=new PSb(xRc,33,ONc,kYb);nSb=new PSb(yRc,34,YNc,oYb);qSb=new PSb("LET", +35,"let",sYb);vSb=new PSb(zRc,36,FRc,JYb);wSb=new PSb(ARc,37,GRc,RYb);xSb=new PSb(BRc,38,HRc,SYb);ySb=new PSb(CRc,39,IRc,TYb);ASb=new PSb("STATIC",40,KRc,fZb);NSb=new PSb("YIELD",41,RNc,zZb);uSb=new PSb(NSc,42,jLc,CYb);FSb=new PSb(OSc,43,TNc,pZb);eSb=new PSb(PSc,44,SNc,dYb);YRb=new PSb(QSc,45,"declare",SXb);HSb=new PSb(ERc,46,$Nc,rZb);rSb=new PSb("MODULE",47,mNc,wYb);sSb=new PSb(RSc,48,"namespace",xYb);a=(oo(),new hs);b=new kCc(XR);for(d=XSb(),e=0,f=d.length;e1&&Tt(Wt(new eyc(a.B.a),new lnb));for(e=new Tu(wu($t(Tt(Wt(new eyc(a.B.a),new lnb)).a),new Xt));Ru(e);){d=Su(e);n=Sd(x8(d,a));if(b){tCb(new uCb(a),a.o,a.v);if(a.g.a.d!=0)return null}if(a.G.kb||!a.G.Sc.a.a.Ud()){s=new dxb(d.i.a,a.G.$!=z9);t=new dpb(a,s,new Ajb(a));Yob(t,n)}if(oqb(n)){C8(d);q8b(a.o,n)}else q8b(a.v,n)}if(a.g.a.d!=0)return null;return a.i}finally{x6(a);r=vZ(mZ(Date.now()),u.a);a.G.pd!=Uab&&!!a.O&& +xsb(a.O,jOc,r)}}function Hbc(){Hbc=JZ;mbc=new Ibc("JSDOC_INFO",0);Ebc=new Ibc("VAR_ARGS",1);abc=new Ibc("INCRDECR",2);ubc=new Ibc("QUOTED",3);qbc=new Ibc("OPT_ARG",4);Abc=new Ibc("SYNTHETIC",5);Eac=new Ibc("ADDED_BLOCK",6);sbc=new Ibc("ORIGINALNAME",7);wbc=new Ibc("SIDE_EFFECT_FLAGS",8);dbc=new Ibc("IS_CONSTANT_NAME",9);jbc=new Ibc("IS_NAMESPACE",10);Qac=new Ibc("DIRECTIVES",11);Rac=new Ibc("DIRECT_EVAL",12);Wac=new Ibc("FREE_CALL",13);ybc=new Ibc("SOURCE_FILE",14);cbc=new Ibc("INPUT_ID",15);xbc= +new Ibc("SLASH_V",16);bbc=new Ibc("INFERRED",17);Hac=new Ibc("CHANGE_TIME",18);vbc=new Ibc("REFLECTED_OBJECT",19);zbc=new Ibc("STATIC_MEMBER",20);Xac=new Ibc("GENERATOR_FN",21);Fac=new Ibc("ARROW_FN",22);Gac=new Ibc("ASYNC_FN",23);Gbc=new Ibc("YIELD_ALL",24);Uac=new Ibc("EXPORT_DEFAULT",25);Tac=new Ibc("EXPORT_ALL_FROM",26);ebc=new Ibc("IS_CONSTANT_VAR",27);gbc=new Ibc("IS_GENERATOR_MARKER",28);hbc=new Ibc("IS_GENERATOR_SAFE",29);Jac=new Ibc("COMPUTED_PROP_METHOD",30);Iac=new Ibc("COMPUTED_PROP_GETTER", +31);Kac=new Ibc("COMPUTED_PROP_SETTER",32);Lac=new Ibc("COMPUTED_PROP_VARIABLE",33);Nac=new Ibc("DECLARED_TYPE_EXPR",34);Dbc=new Ibc("TYPE_BEFORE_CAST",35);rbc=new Ibc("OPT_ES6_TYPED",36);Yac=new Ibc(wTc,37);_ac=new Ibc(xRc,38);Mac=new Ibc("CONSTRUCT_SIGNATURE",39);Dac=new Ibc("ACCESS_MODIFIER",40);pbc=new Ibc("NON_INDEXABLE",41);tbc=new Ibc("PARSE_RESULTS",42);Zac=new Ibc("GOOG_MODULE",43);$ac=new Ibc("GOOG_MODULE_REQUIRE",44);Vac=new Ibc("FEATURE_SET",45);ibc=new Ibc("IS_MODULE_NAME",46);Fbc=new Ibc("WAS_PREVIOUSLY_PROVIDED", +47);fbc=new Ibc("IS_ES6_CLASS",48);Bbc=new Ibc("TRANSPILED",49);Pac=new Ibc("DELETED",50);nbc=new Ibc("MODULE_ALIAS",51);lbc=new Ibc("IS_UNUSED_PARAMETER",52);obc=new Ibc(DOc,53);kbc=new Ibc("IS_SHORTHAND_PROPERTY",54);Sac=new Ibc("ES6_MODULE",55);Cbc=new Ibc("TYPEDEF_TYPE",56);Oac=new Ibc("DEFINE_NAME",57)}function E_(a,b){var c,d,e,f,g,h,i,j,k,l,m,n,o;switch(b.n.f){case 122:F_(a,(IRb(),kRb),b);d=C8b(b);0!=d&&D0(OMc+d,b);return;case 99:F_(a,(IRb(),BRb),b);e=C8b(b);0!=e&&D0(OMc+e,b);return;case 34:case 32:case 33:case 35:f= +C8b(b);0!=f&&D0(OMc+f,b);return;case 24:case 21:case 22:case 19:case 25:case 79:case 20:case 92:g=C8b(b);1!=g&&D0(UMc+g,b);E_(a,b.c);return;case 63:case 64:c=C8b(b);1!=c&&D0(UMc+c,b);N_(a,b.c);return;case 47:h=Cfc(b.n);h!=-1&&(i=C8b(b),h!=i&&D0(QMc+h+RMc+i,b));R_(a,b.n,b.c);E_(a,b.c?b.c.i:null);return;case 59:F_(a,(IRb(),WQb),b);v_(a,b);return;case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:case 58:v_(a,b);return;case 60:j=C8b(b);3!=j&&D0("Expected 3 children, but was "+ +j,b);k=b.c;E_(a,k);E_(a,k.f);E_(a,b.c?b.c.i:null);return;case 31:s0(b);return;case 30:d0(b);return;case 29:W_(b);return;case 18:F_(a,(IRb(),WQb),b);m_(a,b);return;case 27:case 46:case 61:case 62:case 1:case 2:case 3:case 4:case 5:case 36:case 37:case 6:case 8:case 7:case 9:case 41:case 40:case 10:case 11:case 12:case 14:case 13:case 15:case 17:case 16:m_(a,b);return;case 26:I_(a,b);return;case 42:k_(a,b);return;case 43:e0(a,b);return;case 38:l0(b);return;case 28:o_(a,b);return;case 115:p0(a,b);return; +case 23:a0(a,b);return;case 65:G_(a,b,false);return;case 96:r_(a,b,false);return;case 118:v0(a,b);return;case 117:F_(a,(IRb(),CRb),b);b0((Afc(),hfc),b);l=Cfc(b.n);l!=-1&&(m=C8b(b),l!=m&&D0(QMc+l+RMc+m,b));E_(a,b.c);v0(a,b.c?b.c.i:null);return;case 103:F_(a,(IRb(),$Qb),b);b0((Afc(),zfc),b);n=C8b(b);(n<0||n>1)&&D0("Expected child count in [0, 1], but was "+n,b);!!b.c&&E_(a,b.c);return;case 104:F_(a,(IRb(),HQb),b);b0((Afc(),Pcc),b);o=(ppb(),Gpb(b,new frb(Jdc)));(!o||!(o.n==Jdc&&E8b(o,(Hbc(),Gac))!=0))&& +D0("'await' expression is not within an async function",b);return;default:D0("Expected expression but was "+b.n,b)}}function d2(){d2=JZ;Y1=new Mcb("JSC_DEPRECATED_VAR",(E2(),C2),new Rtc("Variable {0} has been deprecated."));Z1=new Mcb("JSC_DEPRECATED_VAR_REASON",C2,new Rtc("Variable {0} has been deprecated: {1}"));$1=new Mcb("JSC_DEPRECATED_PROP",C2,new Rtc("Property {0} of type {1} has been deprecated."));_1=new Mcb("JSC_DEPRECATED_PROP_REASON",C2,new Rtc("Property {0} of type {1} has been deprecated: {2}")); +W1=new Mcb("JSC_DEPRECATED_CLASS",C2,new Rtc("Class {0} has been deprecated."));X1=new Mcb("JSC_DEPRECATED_CLASS_REASON",C2,new Rtc("Class {0} has been deprecated: {1}"));P1=new Mcb("JSC_BAD_PACKAGE_PROPERTY_ACCESS",B2,new Rtc("Access to package-private property {0} of {1} not allowed here."));Q1=new Mcb("JSC_BAD_PRIVATE_GLOBAL_ACCESS",B2,new Rtc("Access to private variable {0} not allowed outside file {1}."));R1=new Mcb("JSC_BAD_PRIVATE_PROPERTY_ACCESS",D2,new Rtc("Access to private property {0} of {1} not allowed here.")); +S1=new Mcb("JSC_BAD_PROTECTED_PROPERTY_ACCESS",D2,new Rtc("Access to protected property {0} of {1} not allowed here."));new Mcb("JSC_BAD_PROPERTY_OVERRIDE_IN_FILE_WITH_FILEOVERVIEW_VISIBILITY",B2,new Rtc("Overridden property {0} in file with fileoverview visibility {1} must explicitly redeclare superclass visibility"));b2=new Mcb("JSC_PRIVATE_OVERRIDE",D2,new Rtc("Overriding private property of {0}."));a2=new Mcb("JSC_EXTEND_FINAL_CLASS",B2,new Rtc("{0} is not allowed to extend final class {1}.")); +c2=new Mcb("JSC_VISIBILITY_MISMATCH",D2,new Rtc("Overriding {0} property of {1} with {2} property."));U1=new Mcb("JSC_CONSTANT_PROPERTY_REASSIGNED_VALUE",D2,new Rtc("constant property {0} assigned a value more than once"));T1=new Mcb("JSC_CONSTANT_PROPERTY_DELETED",D2,new Rtc("constant property {0} cannot be deleted"));V1=new Mcb("JSC_CONVENTION_MISMATCH",D2,new Rtc("Declared access conflicts with access convention."))}function Igb(a,b,c,d,e,f){var g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,A,B,C,D,F,G,H, +I,J,K,L,M,N,O,P;Kob(b);N=LPc+a.b++;G=null;C=null;if(!!c.c&&(c.c?c.c.i:null).n==(Afc(),Sec)){C=new fxc;G=LPc+a.b++}else if(a.f==(Wgb(),Vgb))if(!uwc(a.e).a){--a.b;return}M=I9b(q3b((I=(ae(Hsc(N,_sc(46))==-1,HMc,N),n8b(),new Wbc((Afc(),rec),N)),I),z8b(d),ufc),c);if(e.n==gdc){s=new C7b(false);w6b(s);q9b(M,k6b(s))}p8b(f.g,M,f);for(h=c.c;h;h=w){w=h.f;if(h.n==cfc){O=(J=(ae(Hsc(N,_sc(46))==-1,HMc,N),new Wbc(rec,N)),J);o=h.Yh()?(H=w3b(O,e_(h.Wh())),H):O$(O,h.Wh());P=g9b(h);if(P.n==mdc){t=g9b(P);j=g9b(P);v= +Cgb(o,j)}else{t=P;v=o}if(C){B=e_(h.Wh());h.Yh()&&c9b(B,(Hbc(),ubc),1);C.a[C.a.length]=B}}else if(h.n==fdc){p=(h.c?h.c.i:null).n==mdc;A=g9b(h);if(p){i=h.c?h.c.i:null;t=g9b(i);j=g9b(i)}else{t=g9b(h);j=null}if(C){m=LPc+a.b++;l=(H=(ae(Hsc(m,_sc(46))==-1,HMc,m),new Wbc(rec,m)),H);k=q3b(Tbc(l,false),A,ufc);I9b(k,h);p8b(f.g,k,f);A=Tbc(l,false);Ywc(C,Tbc(l,false))}if(p){n=(J=w3b((K=(ae(Hsc(N,_sc(46))==-1,HMc,N),new Wbc(rec,N)),K),A),J);r=LPc+a.b++;q=q3b((L=(ae(Hsc(r,_sc(46))==-1,HMc,r),new Wbc(rec,r)),L), +n,ufc);I9b(q,h);p8b(f.g,q,f);v=Cgb((H=(ae(Hsc(r,_sc(46))==-1,HMc,r),new Wbc(rec,r)),H),j)}else v=(J=w3b((H=(ae(Hsc(N,_sc(46))==-1,HMc,N),new Wbc(rec,N)),H),A),J)}else if(h.n==Sec){if(w)throw gZ(new Irc("object rest may not be followed by any properties"));g=(J=Tqb(a_(Se(Ze(),MMc)),CE(xE(HU,1),IMc,7,0,[])),J);q8b(g,(K=P3b(CE(xE(HU,1),IMc,7,0,[])),K));q8b(g,(L=(ae(Hsc(N,_sc(46))==-1,HMc,N),new Wbc(rec,N)),L));F=q3b((H=(ae(Hsc(G,_sc(46))==-1,HMc,G),new Wbc(rec,G)),H),g,ufc);I9b(F,c);o8b(f.g,F,M);D=(Xd(!!h.c&& +!h.c.f),h.c);t=X$(D.Wh());v=Fgb(c,h,G,C)}else throw gZ(new Irc("unexpected child"));ppb();if(!!e&&(e.n==ufc||e.n==jec||e.n==gdc))u=q3b(t,v,e.n);else if(e.n==Ccc)u=t3b((H=h3b(t,v),H));else throw gZ(new Irc("not reached"));I9b(u,h);p8b(f.g,u,f);Lgb(a,b,t,t.g)}z8b(f);Vob(b)}function K3(){K3=JZ;new Mcb("JSC_GOOG_MODULE_INVALID_MODULE_NAMESPACE",(E2(),B2),new Rtc("goog.module parameter must be string literals"));new Mcb("JSC_GOOG_MODULE_INVALID_PROVIDE_NAMESPACE",B2,new Rtc("goog.provide parameter must be a string literal.")); +new Mcb("JSC_GOOG_MODULE_INVALID_PROVIDE_CALL",B2,new Rtc("goog.provide can not be called in goog.module."));new Mcb("JSC_GOOG_MODULE_INVALID_GET_ALIAS",B2,new Rtc("goog.module.get should not be aliased."));new Mcb("JSC_GOOG_MODULE_INVALID_EXPORT_COMPUTED_PROPERTY",B2,new Rtc("Computed properties are not yet supported in goog.module exports."));J3=new Mcb("JSC_USELESS_USE_STRICT_DIRECTIVE",C2,new Rtc("'use strict' is unnecessary in goog.module files."));new Mcb("JSC_DUPLICATE_MODULE",B2,new Rtc("Duplicate module: {0}")); +new Mcb("JSC_DUPLICATE_NAMESPACE",B2,new Rtc("Duplicate namespace: {0}"));new Mcb(hNc,B2,new Rtc(iNc));new Mcb("JSC_IMPORT_INLINING_SHADOWS_VAR",B2,new Rtc('Inlining of reference to import "{1}" shadows var "{0}".'));new Mcb("JSC_ILLEGAL_DESTRUCTURING_DEFAULT_EXPORT",B2,new Rtc("Destructuring import only allowed for importing module with named exports.\nSee https://github.com/google/closure-compiler/wiki/goog.module-style"));new Mcb("JSC_ILLEGAL_DESTRUCTURING_NOT_EXPORTED",B2,new Rtc('Destructuring import reference to name "{0}" was not exported in module {1}')); +bn();new Zz(ANc);x3b((ae(Hsc(kNc,_sc(46))==-1,HMc,kNc),n8b(),new Wbc((Afc(),rec),kNc)),new Wbc(bfc,oNc));x3b((ae(Hsc(kNc,_sc(46))==-1,HMc,kNc),new Wbc(rec,kNc)),new Wbc(bfc,BNc));I3=x3b((ae(Hsc(kNc,_sc(46))==-1,HMc,kNc),new Wbc(rec,kNc)),new Wbc(bfc,mNc));x3b(I3,new Wbc(bfc,"declareLegacyNamespace"));x3b(x8b(I3,false),new Wbc(bfc,nNc));x3b((ae(Hsc(kNc,_sc(46))==-1,HMc,kNc),new Wbc(rec,kNc)),new Wbc(bfc,CNc));x3b((ae(Hsc(kNc,_sc(46))==-1,HMc,kNc),new Wbc(rec,kNc)),new Wbc(bfc,lNc));x3b((ae(Hsc(kNc, +_sc(46))==-1,HMc,kNc),new Wbc(rec,kNc)),new Wbc(bfc,pNc))}function Cdb(a,b,c){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,A,B,C,D,F,G,H,I,J,K,L;d=c.a;A=c.b;if(A.a.length==0)return;h=H8b(d,(Hbc(),ybc));if(!!h&&h.Kh())for(v=new wxc(A);v.aa.f.b,k||zVb(a,0,ZYb)||zVb(a,0,KXb)||zVb(a,0,XXb)||(j=(l=DWb(a.g,0).e,iYb==l||a.b.d&&VSb(l)||!a.b.c&&USb(l)?kTb(a):null)),nTb(a),new q$b(new Z1b(i,a.f),j);case 3:return m=DWb(a.g,0).d.b,iTb(a,(AZb(),DXb)),n=null,o=DWb(a.g,0).d.b.b>a.f.b,o||zVb(a,0,ZYb)||zVb(a,0,KXb)||zVb(a,0,XXb)||(n=(p=DWb(a.g,0).e,iYb==p||a.b.d&&VSb(p)||!a.b.c&&USb(p)?kTb(a):null)),nTb(a),new TZb(new Z1b(m, +a.f),n);case 19:return q=DWb(a.g,0).d.b,iTb(a,(AZb(),WYb)),r=null,s=DWb(a.g,0).d.b.b>a.f.b,s||zVb(a,0,ZYb)||zVb(a,0,KXb)||zVb(a,0,XXb)||(r=xTb(a,1)),nTb(a),new u1b(new Z1b(q,a.f),r);case 28:return t=DWb(a.g,0).d.b,iTb(a,(AZb(),yZb)),iTb(a,GYb),u=xTb(a,1),iTb(a,LXb),v=hVb(a),new Q1b(new Z1b(t,a.f),u,v);case 20:return w=DWb(a.g,0).d.b,iTb(a,(AZb(),iZb)),iTb(a,GYb),A=xTb(a,1),iTb(a,LXb),iTb(a,FYb),B=UTb(a),iTb(a,KXb),new y1b(new Z1b(w,a.f),A,B);case 22:return C=DWb(a.g,0).d.b,iTb(a,(AZb(),nZb)),D=null, +F=DWb(a.g,0).d.b.b>a.f.b,F||zVb(a,0,ZYb)||zVb(a,0,KXb)||zVb(a,0,XXb)?VVb(a,"semicolon/newline not allowed after 'throw'",CE(xE(iW,1),mLc,1,5,[])):D=xTb(a,1),nTb(a),new D1b(new Z1b(C,a.f),D);case 23:return G=DWb(a.g,0).d.b,iTb(a,(AZb(),qZb)),H=STb(a),I=null,AVb(a,HXb)&&(I=VTb(a)),J=null,AVb(a,eYb)&&(J=(P=DWb(a.g,0).d.b,iTb(a,eYb),Q=STb(a),new B$b(new Z1b(P,a.f),Q))),!I&&!J&&VVb(a,"'catch' or 'finally' expected.",CE(xE(iW,1),mLc,1,5,[])),new E1b(new Z1b(G,a.f),H,I,J);case 7:return K=DWb(a.g,0).d.b, +iTb(a,(AZb(),RXb)),nTb(a),new r$b(new Z1b(K,a.f));default:{b=DWb(a.g,0).e;if(((AZb(),iYb)==b||a.b.d&&VSb(b)||!a.b.c&&USb(b))&&zVb(a,1,NXb))return L=DWb(a.g,0).d.b,M=jTb(a),iTb(a,NXb),new i_b(new Z1b(L,a.f),M,hVb(a))}return N=DWb(a.g,0).d.b,O=xTb(a,1),nTb(a),new A$b(new Z1b(N,a.f),O)}}function q0(a,b,c){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,A,B,C,D,F,G,H,I,J,K,L,M;switch(b.n.f){case 83:b0((Afc(),eec),b);i=Cfc(b.n);i!=-1&&(j=C8b(b),i!=j&&D0(QMc+i+RMc+j,b));S_(b.c);q0(a,b.c?b.c.i:null,false);return; +case 82:n_(a,b);return;case 65:c?G_(a,b,true):(b0((Afc(),Jdc),b),k=Cfc(b.n),k!=-1&&(l=C8b(b),k!=l&&D0(QMc+k+RMc+l,b)),W_(b.c),k0(a,b.c.f),n_(a,b.c?b.c.i:null),H_(a,b),b.g.n==Vcc&&(!b.g?null:b.g.g).n!=Jdc&&F_(a,(IRb(),KQb),b),undefined);return;case 77:b0((Afc(),yfc),b);m=Cfc(b.n);m!=-1&&(n=C8b(b),m!=n&&D0(QMc+m+RMc+n,b));E_(a,b.c);n_(a,b.c?b.c.i:null);return;case 72:b0((Afc(),Fdc),b);o=C8b(b);4!=o&&D0("Expected 4 children, but was "+o,b);A0(a,b.c);j0(a,b.c.f);j0(a,B8b(b,2));n_(a,b.c?b.c.i:null);return; +case 73:b0((Afc(),Hdc),b);p=Cfc(b.n);p!=-1&&(q=C8b(b),p!=q&&D0(QMc+p+RMc+q,b));z0(a,b.c);E_(a,b.c.f);n_(a,b.c?b.c.i:null);return;case 101:F_(a,(IRb(),ZQb),b);b0((Afc(),Idc),b);r=Cfc(b.n);r!=-1&&(s=C8b(b),r!=s&&D0(QMc+r+RMc+s,b));z0(a,b.c);E_(a,b.c.f);n_(a,b.c?b.c.i:null);return;case 102:F_(a,(IRb(),YQb),b);F_(a,HQb,b);b0((Afc(),Gdc),b);t=Cfc(b.n);t!=-1&&(u=C8b(b),t!=u&&D0(QMc+t+RMc+u,b));z0(a,b.c);E_(a,b.c.f);n_(a,b.c?b.c.i:null);return;case 70:b0((Afc(),xfc),b);v=Cfc(b.n);v!=-1&&(w=C8b(b),v!=w&& +D0(QMc+v+RMc+w,b));E_(a,b.c);n_(a,b.c?b.c.i:null);return;case 71:b0((Afc(),qdc),b);A=Cfc(b.n);A!=-1&&(B=C8b(b),A!=B&&D0(QMc+A+RMc+B,b));n_(a,b.c);E_(a,b.c?b.c.i:null);return;case 67:t0(a,b);return;case 66:b0((Afc(),Tdc),b);C=C8b(b);(C<2||C>3)&&D0(ZMc+C,b);E_(a,b.c);n_(a,b.c.f);C8b(b)==3&&n_(a,b.c?b.c.i:null);return;case 88:for(e=(!b.c?(Cyc(),Cyc(),Byc):new Kbc(b.c)).Vd();e.Id();){d=e.Jd();d.n==(Afc(),odc)?(g=C8b(d),2!=g&&D0(PMc+g,d)):(h=C8b(d),1!=h&&D0(UMc+h,d))}case 76:case 100:Y_(a,b.n,b);return; +case 84:b0((Afc(),Ddc),b);D=Cfc(b.n);D!=-1&&(F=C8b(b),D!=F&&D0(QMc+D+RMc+F,b));E_(a,b.c);return;case 0:b0((Afc(),Uec),b);T_(b);!!b.c&&E_(a,b.c);return;case 39:b0((Afc(),mfc),b);G=Cfc(b.n);G!=-1&&(H=C8b(b),G!=H&&D0(QMc+G+RMc+H,b));E_(a,b.c);return;case 44:x0(a,b);return;case 74:b0((Afc(),Xcc),b);T_(b);!!b.c&&S_(b.c);return;case 75:b0((Afc(),hdc),b);T_(b);!!b.c&&S_(b.c);return;case 80:case 89:f=C8b(b);0!=f&&D0(OMc+f,b);return;case 96:r_(a,b,c);W_(b.c);return;case 105:K_(a,b);return;case 109:B_(a,b, +c);return;case 151:F_(a,(IRb(),dRb),b);b0((Afc(),bec),b);I=Cfc(b.n);I!=-1&&(J=C8b(b),I!=J&&D0(QMc+I+RMc+J,b));K=b.c;W_(K);L=K.f;L.n==tdc?(M=C8b(L),0!=M&&D0(OMc+M,L)):O_(L);Q_(a,b.c?b.c.i:null);return;case 154:z_(b);return;case 157:y0(a,b);return;case 158:F_(a,(IRb(),DQb),b);b0((Afc(),kdc),b);j_(a,b.c);return;case 162:Z_(a,b,c);return;default:D0("Expected statement but was "+b.n+".",b)}}function Bmb(a,b){var c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t;g=false;k=new fxc;k.a[k.a.length]="/**";(b.a&sQc)!=0?(k.a[k.a.length]= +"@externs",true):(b.a&16)!=0&&(k.a[k.a.length]="@typeSummary",true);(b.a&1024)!=0?(k.a[k.a.length]="@export",true):!!b.j&&b.j!=(e6b(),_5b)&&Ywc(k,"@"+ge(b.j).toLowerCase());(b.a&tQc)!=0&&(k.a[k.a.length]="@abstract",true);!!(!b.d?null:b.d.i)&&Ywc(k,zmb(a,"lends",(!b.d?null:b.d.i).a));(b.a&1)!=0&&(b.a&4)==0&&(k.a[k.a.length]="@const",true);(b.a&32)!=0&&(k.a[k.a.length]="@final",true);d=!b.d?null:b.d.d;d!=null&&(k.a[k.a.length]="@desc "+d+gMc,true);(b.a&JPc)!=0&&(k.a[k.a.length]="@dict",true);(b.a& +vMc)!=0&&(k.a[k.a.length]="@struct",true);(b.a&IPc)!=0&&(k.a[k.a.length]="@unrestricted ",true);(b.a&2)!=0&&(k.a[k.a.length]="@constructor",true);((b.a&512)!=0||(b.a&bPc)!=0)&&(b.a&bPc)==0&&(k.a[k.a.length]="@interface",true);((b.a&512)!=0||(b.a&bPc)!=0)&&(b.a&bPc)!=0&&(k.a[k.a.length]="@record",true);if(!b.d?null:b.d.a){g=true;s=Cmb((!b.d?null:b.d.a).a);Ywc(k,zmb(a,NNc,s))}for(r=A4b(b).Vd();r.Id();){p=r.Jd();g=true;s=Cmb(p.a);Ywc(k,zmb(a,NNc,s))}for(q=C4b(b).Vd();q.Id();){p=q.Jd();g=true;s=Cmb(p.a); +Ywc(k,zmb(a,ONc,s))}if(b.g){g=true;s=Cmb(b.g.a);Ywc(k,zmb(a,FMc,s))}if(F4b(b)>0){g=true;for(i=G4b(b).Vd();i.Id();){h=i.Jd();Ywc(k,"@param "+Amb(a,h,H4b(b,h)))}}if((b.a&HPc)==$Lc){g=true;Ywc(k,zmb(a,ENc,((HPc&b.a)==$Lc?b.i:null).a))}K4b(b).Ud()||Ywc(k,zmb(a,uQc,K4b(b).ce(0).a));j=J4b(b);if(!j.Ud()){Ywc(k,vQc+Nc(new Pc(String.fromCharCode(44)),j.Vd()));g=true}t=O4b(b);if(!t.Ud()){g=true;for(f=po(t).Vd();f.Id();){e=f.Jd();h=e.Ve();o=e5(new j5(e.We()));k.a[k.a.length]=vQc+h+" := "+o+" =:"}}(b.a&64)!= +0&&(k.a[k.a.length]="@override",true);(b.a&HPc)==WPc&&(b.a&4)==0&&(b.e?Ywc(k,Dmb(a,((HPc&b.a)==WPc?b.i:null).a)):Ywc(k,zmb(a,$Nc,((HPc&b.a)==WPc?b.i:null).a)));(b.a&4)!=0&&Ywc(k,zmb(a,"define",((HPc&b.a)==WPc?b.i:null).a));(b.a&HPc)==hMc&&Ywc(k,zmb(a,"typedef",((HPc&b.a)==hMc?b.i:null).a));(b.a&HPc)==eQc&&Ywc(k,zmb(a,ZNc,((HPc&b.a)==eQc?b.i:null).a));(b.a&8192)!=0&&(k.a[k.a.length]="@implicitCast",true);(b.a&hQc)!=0&&(k.a[k.a.length]="@nocollapse",true);n=(m=!b.d?null:b.d.q,!m?(Cyc(),Cyc(),Byc):m); +if(!n.Ud()){c=n.ae(zE(nW,uNc,2,0,6,1));Wxc(c,(Wy(),Vy));Ywc(k,"@suppress {"+Nc(new Pc(String.fromCharCode(44)),new Guc(new eyc(c)))+"}");g=true}if((b.a&256)!=0){Ywc(k,"@deprecated "+(!b.d?null:b.d.c));g=true}if(!!b.d&&J5b(b.d,3)){g=true;k.a[k.a.length]="@polymer"}if(!!b.d&&J5b(b.d,2)){g=true;k.a[k.a.length]="@polymerBehavior"}if(!!b.d&&J5b(b.d,6)){g=true;k.a[k.a.length]="@mixinFunction"}if(!!b.d&&J5b(b.d,5)){g=true;k.a[k.a.length]="@mixinClass"}if(!!b.d&&J5b(b.d,4)){g=true;k.a[k.a.length]="@customElement"}k.a[k.a.length]= +"*/";l=new stc;g?Lc(new Pc("\n "),l,new wxc(k)):Lc(new Pc(" "),l,new wxc(k));l.a+=g?gMc:" ";return l.a}function TIb(){TIb=JZ;oIb=new UIb("NG_INJECT",0);LHb=new UIb("ABSTRACT",1);MHb=new UIb("AUTHOR",2);NHb=new UIb("CLOSURE_PRIMITIVE",3);OHb=new UIb("CONSISTENTIDGENERATOR",4);PHb=new UIb("CONSTANT",5);QHb=new UIb(tRc,6);RHb=new UIb("CUSTOM_ELEMENT",7);DIb=new UIb("RECORD",8);SHb=new UIb("DEFINE",9);THb=new UIb("DEPRECATED",10);UHb=new UIb("DESC",11);VHb=new UIb("DICT",12);WHb=new UIb("DISPOSES",13); +XHb=new UIb(uRc,14);$Hb=new UIb(vRc,15);_Hb=new UIb("EXTERNS",16);YHb=new UIb(wRc,17);ZHb=new UIb("EXPOSE",18);aIb=new UIb("FILE_OVERVIEW",19);bIb=new UIb("FINAL",20);cIb=new UIb("HIDDEN",21);dIb=new UIb("IDGENERATOR",22);eIb=new UIb(xRc,23);fIb=new UIb("IMPLICIT_CAST",24);gIb=new UIb("INHERIT_DOC",25);hIb=new UIb(yRc,26);iIb=new UIb("LENDS",27);jIb=new UIb("LICENSE",28);kIb=new UIb("MEANING",29);lIb=new UIb("MIXIN_CLASS",30);mIb=new UIb("MIXIN_FUNCTION",31);nIb=new UIb("MODIFIES",32);qIb=new UIb("NO_COLLAPSE", +33);rIb=new UIb("NO_COMPILE",34);sIb=new UIb("NO_INLINE",35);tIb=new UIb("NO_SIDE_EFFECTS",36);pIb=new UIb("NOT_IMPLEMENTED",37);uIb=new UIb("OVERRIDE",38);vIb=new UIb(zRc,39);wIb=new UIb("PARAM",40);xIb=new UIb("POLYMER",41);yIb=new UIb("POLYMER_BEHAVIOR",42);zIb=new UIb("PRESERVE",43);AIb=new UIb(ARc,44);BIb=new UIb(BRc,45);CIb=new UIb(CRc,46);EIb=new UIb(DRc,47);FIb=new UIb("SEE",48);GIb=new UIb("STABLEIDGENERATOR",49);HIb=new UIb("STRUCT",50);IIb=new UIb("SUPPRESS",51);JIb=new UIb("TEMPLATE", +52);KIb=new UIb(EMc,53);LIb=new UIb("THROWS",54);MIb=new UIb(ERc,55);NIb=new UIb("TYPEDEF",56);OIb=new UIb("TYPE_SUMMARY",57);PIb=new UIb("UNRESTRICTED",58);QIb=new UIb("VERSION",59);RIb=new UIb("WIZACTION",60);SIb=es(new hs,"ngInject",oIb).Lf("abstract",LHb).Lf("argument",wIb).Lf("author",MHb).Lf("closurePrimitive",NHb).Lf("consistentIdGenerator",OHb).Lf(XOc,PHb).Lf("constant",PHb).Lf(LMc,QHb).Lf("customElement",RHb).Lf("copyright",jIb).Lf("define",SHb).Lf(SOc,THb).Lf("desc",UHb).Lf("dict",VHb).Lf("disposes", +WHb).Lf(ZNc,XHb).Lf(INc,YHb).Lf("expose",ZHb).Lf(NNc,$Hb).Lf("externs",_Hb).Lf("fileoverview",aIb).Lf("final",bIb).Lf("hidden",cIb).Lf("idGenerator",dIb).Lf(ONc,eIb).Lf("implicitCast",fIb).Lf("inheritDoc",gIb).Lf(YNc,hIb).Lf("record",DIb).Lf("lends",iIb).Lf("license",jIb).Lf("meaning",kIb).Lf("mixinClass",lIb).Lf("mixinFunction",mIb).Lf("modifies",nIb).Lf("nocollapse",qIb).Lf("nocompile",rIb).Lf("noinline",sIb).Lf("nosideeffects",tIb).Lf("override",uIb).Lf("owner",MHb).Lf(FRc,vIb).Lf("param",wIb).Lf("polymer", +xIb).Lf($Oc,yIb).Lf("preserve",zIb).Lf(GRc,AIb).Lf(HRc,BIb).Lf(IRc,CIb).Lf(ENc,EIb).Lf("returns",EIb).Lf("see",FIb).Lf("stableIdGenerator",GIb).Lf("struct",HIb).Lf("suppress",IIb).Lf("template",JIb).Lf(FMc,KIb).Lf(uQc,LIb).Lf($Nc,MIb).Lf("typedef",NIb).Lf("typeSummary",OIb).Lf("unrestricted",PIb).Lf("version",QIb).Lf("wizaction",RIb).Kf()}function Ftb(){Ftb=JZ;new Mcb("JSC_NULL_ARGUMENT_ERROR",(E2(),B2),new Rtc('method "{0}" called without an argument'));new Mcb("JSC_EXPECTED_OBJECTLIT_ERROR",B2, +new Rtc('method "{0}" expected an object literal argument'));new Mcb("JSC_EXPECTED_STRING_ERROR",B2,new Rtc('method "{0}" expected a string argument'));new Mcb("JSC_INVALID_ARGUMENT_ERROR",B2,new Rtc('method "{0}" called with invalid argument'));new Mcb("JSC_INVALID_CSS_NAME_MAP_STYLE_ERROR",B2,new Rtc("Invalid CSS name map style {0}"));new Mcb("JSC_TOO_MANY_ARGUMENTS_ERROR",B2,new Rtc('method "{0}" called with more than one argument'));new Mcb("JSC_DUPLICATE_NAMESPACE_ERROR",B2,new Rtc('namespace "{0}" cannot be provided twice\nOriginally provided at {1}')); +new Mcb("JSC_WEAK_NAMESPACE_TYPE",D2,new Rtc("Provided symbol declared with type Object. This is rarely useful. For more information see https://github.com/google/closure-compiler/wiki/A-word-about-the-type-Object"));new Mcb("JSC_CLASS_NAMESPACE_ERROR",B2,new Rtc("\"{0}\" cannot be both provided and declared as a class. Try var {0} = class '{'...'}'"));new Mcb("JSC_FUNCTION_NAMESPACE_ERROR",B2,new Rtc('"{0}" cannot be both provided and declared as a function'));Dtb=new Mcb("JSC_MISSING_PROVIDE_ERROR", +B2,new Rtc('required "{0}" namespace never provided'));Ctb=new Mcb(hNc,B2,new Rtc('required "{0}" namespace not provided yet'));Btb=new Mcb("JSC_INVALID_PROVIDE_ERROR",B2,new Rtc('"{0}" is not a valid {1} qualified name'));new Mcb("JSC_INVALID_DEFINE_NAME_ERROR",B2,new Rtc('"{0}" is not a valid JS identifier name'));new Mcb("JSC_INVALID_MISSING_DEFINE_ANNOTATION",B2,new Rtc("Missing @define annotation"));new Mcb("JSC_XMODULE_REQUIRE_ERROR",D2,new Rtc('namespace "{0}" is required in module {2} but provided in module {1}. Is module {2} missing a dependency on module {1}?')); +new Mcb("JSC_NON_STRING_PASSED_TO_SET_CSS_NAME_MAPPING_ERROR",B2,new Rtc("goog.setCssNameMapping only takes an object literal with string values"));new Mcb("INVALID_CSS_RENAMING_MAP",D2,new Rtc("Invalid entries in css renaming map: {0}"));new Mcb("JSC_GOOG_BASE_CLASS_ERROR",B2,new Rtc("incorrect use of goog.base: {0}"));new Mcb("JSC_BASE_CLASS_ERROR",B2,new Rtc("incorrect use of {0}.base: {1}"));new Mcb("JSC_CLOSURE_DEFINES_ERROR",B2,new Rtc("Invalid CLOSURE_DEFINES definition"));new Mcb("JSC_INVALID_FORWARD_DECLARE", +B2,new Rtc("Malformed goog.forwardDeclaration"));Etb=new Mcb("JSC_USE_OF_GOOG_BASE",C2,new Rtc("goog.base is not compatible with ES5 strict mode.\nPlease use an alternative.\nFor EcmaScript classes use the super keyword. For traditional Closure classes,\nuse the class specific base method instead. For example, for the constructor MyClass:\n MyClass.base(this, ''constructor'')"));ztb=new Mcb(HQc,B2,new Rtc("Closure primitive method {0} may not be aliased"));Atb=new Mcb(HQc,B2,new Rtc("Closure primitive method {0} may not be aliased outside a module (ES module, CommonJS module, or goog.module)"))} +function r3(){r3=JZ;new Mcb("JSC_AT_EXPORT_IN_GOOG_MODULE",(E2(),B2),new Rtc("@export has no effect on top-level names in a goog.module. See http://go/js-practices/exports#export-annotations-in-googmodule Consider using goog.exportSymbol instead."));h3=new Mcb("JSC_AT_EXPORT_IN_NON_LEGACY_GOOG_MODULE",B2,new Rtc("@export is not allowed here in a non-legacy goog.module. Consider using goog.exportSymbol instead."));k3=new Mcb("JSC_GOOG_MODULE_IN_NON_MODULE",B2,new Rtc("goog.module() call must be the first statement in a module.")); +i3=new Mcb("JSC_DECLARE_LEGACY_NAMESPACE_IN_NON_MODULE",B2,new Rtc("goog.module.declareLegacyNamespace may only be called in a goog.module."));new Mcb("JSC_GOOG_MODULE_REFERENCES_THIS",B2,new Rtc("The body of a goog.module cannot reference 'this'."));new Mcb("JSC_GOOG_MODULE_USES_THROW",B2,new Rtc("The body of a goog.module cannot use 'throw'."));new Mcb("JSC_DUPLICATE_NAME_SHORT_REQUIRE",B2,new Rtc("Found multiple goog.require statements importing identifier ''{0}''."));new Mcb("JSC_INVALID_DESTRUCTURING_REQUIRE", +B2,new Rtc("Destructuring goog.require must be a simple object pattern."));o3=new Mcb("JSC_LET_GOOG_REQUIRE",C2,new Rtc(jNc));new Mcb("JSC_MULTIPLE_MODULES_IN_FILE",B2,new Rtc("There should only be a single goog.module() statement per file."));new Mcb("JSC_ONE_REQUIRE_PER_DECLARATION",B2,new Rtc("There may only be one goog.require() per var/let/const declaration."));l3=new Mcb("JSC_INCORRECT_SHORTNAME_CAPITALIZATION",C2,new Rtc("The capitalization of short name {0} is incorrect; it should be {1}.")); +new Mcb("JSC_EXPORT_NOT_AT_MODULE_SCOPE",B2,new Rtc("Exports must be at the top-level of a module"));j3=new Mcb("JSC_EXPORT_NOT_A_STATEMENT",B2,new Rtc("Exports should be a statement."));new Mcb("JSC_EXPORT_REPEATED_ERROR",B2,new Rtc("Name cannot be exported multiple times. Previous export on line {0}."));new Mcb("JSC_REFERENCE_TO_MODULE_GLOBAL_NAME",B2,new Rtc("References to the global name of a module are not allowed. Perhaps you meant exports?"));p3=new Mcb("JSC_REFERENCE_TO_FULLY_QUALIFIED_IMPORT_NAME", +C2,new Rtc("Reference to fully qualified import name ''{0}''. Imports in goog.module should use the return value of goog.require / goog.forwardDeclare instead."));q3=new Mcb("JSC_REFERENCE_TO_SHORT_IMPORT_BY_LONG_NAME_INCLUDING_SHORT_NAME",C2,new Rtc("Reference to fully qualified import name ''{0}''. Please use the short name ''{1}'' instead."));m3=new Mcb("JSC_JSDOC_REFERENCE_TO_FULLY_QUALIFIED_IMPORT_NAME",C2,new Rtc("Reference to fully qualified import name ''{0}'' in JSDoc. Imports in goog.module should use the return value of goog.require / goog.forwardDeclare instead.")); +n3=new Mcb("JSC_JSDOC_REFERENCE_TO_SHORT_IMPORT_BY_LONG_NAME_INCLUDING_SHORT_NAME",C2,new Rtc("Reference to fully qualified import name ''{0}'' in JSDoc. Please use the short name ''{1}'' instead."));new Mcb("JSC_REQUIRE_NOT_AT_TOP_LEVEL",B2,new Rtc("goog.require() must be called at file scope."))}function Efc(a){var b,c,d,e;d=false;e=a;f:{b=null;g:switch(e.length){case 2:c=e.charCodeAt(1);if(c==102){if(e.charCodeAt(0)==105){d=true;break f}}else if(c==110){if(e.charCodeAt(0)==105){d=true;break f}}else if(c== +111)if(e.charCodeAt(0)==100){d=true;break f}break g;case 3:switch(e.charCodeAt(0)){case 102:{if(e.charCodeAt(2)==114&&e.charCodeAt(1)==111){d=true;break f}}break g;case 105:{if(e.charCodeAt(2)==116&&e.charCodeAt(1)==110){d=true;break f}}break g;case 110:{if(e.charCodeAt(2)==119&&e.charCodeAt(1)==101){d=true;break f}}break g;case 116:{if(e.charCodeAt(2)==121&&e.charCodeAt(1)==114){d=true;break f}}break g;case 118:{if(e.charCodeAt(2)==114&&e.charCodeAt(1)==97){d=true;break f}}break g}break g;case 4:switch(e.charCodeAt(0)){case 98:b= +"byte";d=true;break g;case 99:c=e.charCodeAt(3);if(c==101){if(e.charCodeAt(2)==115&&e.charCodeAt(1)==97){d=true;break f}}else if(c==114)if(e.charCodeAt(2)==97&&e.charCodeAt(1)==104){d=true;break f}break g;case 101:c=e.charCodeAt(3);if(c==101){if(e.charCodeAt(2)==115&&e.charCodeAt(1)==108){d=true;break f}}else if(c==109)if(e.charCodeAt(2)==117&&e.charCodeAt(1)==110){d=true;break f}break g;case 103:b="goto";d=true;break g;case 108:b="long";d=true;break g;case 110:b=jLc;d=true;break g;case 116:c=e.charCodeAt(3); +if(c==101){if(e.charCodeAt(2)==117&&e.charCodeAt(1)==114){d=true;break f}}else if(c==115)if(e.charCodeAt(2)==105&&e.charCodeAt(1)==104){d=true;break f}break g;case 118:b=XNc;d=true;break g;case 119:b="with";d=true;break g}break g;case 5:switch(e.charCodeAt(2)){case 97:b=MNc;d=true;break g;case 101:b="break";d=true;break g;case 105:b=PNc;d=true;break g;case 108:b=SNc;d=true;break g;case 110:c=e.charCodeAt(0);if(c==99){b=XOc;d=true}else if(c==102){b="final";d=true}break g;case 111:c=e.charCodeAt(0); +if(c==102){b="float";d=true}else if(c==115){b="short";d=true}break g;case 112:b=DMc;d=true;break g;case 114:b="throw";d=true;break g;case 116:b="catch";d=true;break g}break g;case 6:switch(e.charCodeAt(1)){case 97:b="native";d=true;break g;case 101:c=e.charCodeAt(0);if(c==100){b=ISc;d=true}else if(c==114){b=ENc;d=true}break g;case 104:b=uQc;d=true;break g;case 109:b=LNc;d=true;break g;case 111:b="double";d=true;break g;case 116:b=KRc;d=true;break g;case 117:b=IRc;d=true;break g;case 119:b="switch"; +d=true;break g;case 120:b=INc;d=true;break g;case 121:b=yQc;d=true;break g}break g;case 7:switch(e.charCodeAt(1)){case 97:b=FRc;d=true;break g;case 101:b=JNc;d=true;break g;case 105:b=DNc;d=true;break g;case 111:b=XKc;d=true;break g;case 114:b=GRc;d=true;break g;case 120:b=NNc;d=true;break g}break g;case 8:switch(e.charCodeAt(0)){case 97:b="abstract";d=true;break g;case 99:b=UNc;d=true;break g;case 100:b=WNc;d=true;break g;case 102:b=ZKc;d=true;break g;case 118:b="volatile";d=true;break g}break g; +case 9:c=e.charCodeAt(0);if(c==105){b=YNc;d=true}else if(c==112){b=HRc;d=true}else if(c==116){b="transient";d=true}break g;case 10:c=e.charCodeAt(1);if(c==109){b=ONc;d=true}else if(c==110){b=zQc;d=true}break g;case 12:b="synchronized";d=true;break g}if(b!=null&&b!=e&&!Esc(b,e))return false}return d}function IRb(){IRb=JZ;VQb=new JRb("ES3_KEYWORDS_AS_IDENTIFIERS",0,"ES3 keywords as identifiers",1);aRb=new JRb("GETTER",1,"getters",1);eRb=new JRb("KEYWORDS_AS_PROPERTIES",2,"reserved words as properties", +1);yRb=new JRb("SETTER",3,"setters",1);ARb=new JRb("STRING_CONTINUATION",4,"string continuation",1);DRb=new JRb("TRAILING_COMMA",5,"trailing comma",1);FQb=new JRb("ARRAY_PATTERN_REST",6,"array pattern rest",2);GQb=new JRb("ARROW_FUNCTIONS",7,"arrow function",2);JQb=new JRb("BINARY_LITERALS",8,"binary literal",2);KQb=new JRb("BLOCK_SCOPED_FUNCTION_DECLARATION",9,"block-scoped function declaration",2);MQb=new JRb("CLASSES",10,MNc,2);NQb=new JRb("CLASS_EXTENDS",11,"class extends",2);OQb=new JRb("CLASS_GETTER_SETTER", +12,"class getters/setters",2);PQb=new JRb("COMPUTED_PROPERTIES",13,"computed property",2);RQb=new JRb("CONST_DECLARATIONS",14,"const declaration",2);SQb=new JRb("DEFAULT_PARAMETERS",15,"default parameter",2);EQb=new JRb("ARRAY_DESTRUCTURING",16,"array destructuring",2);lRb=new JRb("OBJECT_DESTRUCTURING",17,"object destructuring",2);XQb=new JRb("EXTENDED_OBJECT_LITERALS",18,"extended object literal",2);ZQb=new JRb("FOR_OF",19,"for-of loop",2);$Qb=new JRb("GENERATORS",20,"generator",2);fRb=new JRb("LET_DECLARATIONS", +21,"let declaration",2);gRb=new JRb("MEMBER_DECLARATIONS",22,"member declaration",2);kRb=new JRb(wSc,23,QNc,2);oRb=new JRb("OCTAL_LITERALS",24,"octal literal",2);sRb=new JRb("REGEXP_FLAG_U",25,"RegExp flag 'u'",2);tRb=new JRb("REGEXP_FLAG_Y",26,"RegExp flag 'y'",2);xRb=new JRb("REST_PARAMETERS",27,"rest parameter",2);zRb=new JRb("SPREAD_EXPRESSIONS",28,"spread expression",2);BRb=new JRb(CMc,29,DMc,2);CRb=new JRb("TEMPLATE_LITERALS",30,"template literal",2);iRb=new JRb("MODULES",31,"modules",2);WQb= +new JRb("EXPONENT_OP",32,"exponent operator (**)",3);HQb=new JRb("ASYNC_FUNCTIONS",33,_Nc,4);ERb=new JRb("TRAILING_COMMA_IN_PARAM_LIST",34,"trailing comma in param list",4);mRb=new JRb("OBJECT_LITERALS_WITH_SPREAD",35,"object literals with spread",5);nRb=new JRb("OBJECT_PATTERN_REST",36,"object pattern rest",5);IQb=new JRb("ASYNC_GENERATORS",37,"async generator functions",5);YQb=new JRb(xSc,38,"for-await-of loop",5);rRb=new JRb("REGEXP_FLAG_S",39,"RegExp flag 's'",5);uRb=new JRb("REGEXP_LOOKBEHIND", +40,"RegExp Lookbehind",5);vRb=new JRb("REGEXP_NAMED_GROUPS",41,"RegExp named groups",5);wRb=new JRb("REGEXP_UNICODE_PROPERTY_ESCAPE",42,"RegExp unicode property escape",5);HRb=new JRb("UNESCAPED_UNICODE_LINE_OR_PARAGRAPH_SEP",43,"Unescaped unicode line or paragraph separator",6);pRb=new JRb("OPTIONAL_CATCH_BINDING",44,"Optional catch binding",6);TQb=new JRb(ySc,45,"Dynamic module import",8);CQb=new JRb("ACCESSIBILITY_MODIFIER",46,"accessibility modifier",9);DQb=new JRb(zSc,47,"ambient declaration", +9);LQb=new JRb(ASc,48,"call signature",9);QQb=new JRb("CONSTRUCTOR_SIGNATURE",49,"constructor signature",9);UQb=new JRb(uRc,50,ZNc,9);_Qb=new JRb("GENERICS",51,"generics",9);bRb=new JRb(xRc,52,ONc,9);cRb=new JRb(BSc,53,"index signature",9);dRb=new JRb(yRc,54,YNc,9);hRb=new JRb("MEMBER_VARIABLE_IN_CLASS",55,"member variable in class",9);jRb=new JRb(CSc,56,"namespace declaration",9);qRb=new JRb(DSc,57,"optional parameter",9);FRb=new JRb(ESc,58,"type alias",9);GRb=new JRb("TYPE_ANNOTATION",59,"type annotation", +9)}function AZb(){AZb=JZ;XXb=new DZb("END_OF_FILE",0,"End of File");aYb=new DZb(zMc,1,"error");iYb=new DZb("IDENTIFIER",2,"identifier");DXb=new CZb("BREAK",3);GXb=new CZb("CASE",4);HXb=new CZb(FSc,5);QXb=new CZb(GSc,6);RXb=new CZb(HSc,7);TXb=new CZb(gRc,8);UXb=new CZb("DELETE",9);VXb=new CZb("DO",10);WXb=new CZb("ELSE",11);eYb=new CZb(JSc,12);fYb=new CZb("FOR",13);gYb=new CZb(jQc,14);jYb=new CZb("IF",15);mYb=new CZb("IN",16);nYb=new CZb(uSc,17);yYb=new CZb("NEW",18);WYb=new CZb(DRc,19);iZb=new CZb(KSc, +20);mZb=new CZb(EMc,21);nZb=new CZb("THROW",22);qZb=new CZb("TRY",23);sZb=new CZb(LSc,24);vZb=new CZb("VAR",25);wZb=new CZb("VOID",26);xZb=new CZb("WHILE",27);yZb=new CZb("WITH",28);IXb=new CZb(iQc,29);PXb=new CZb("CONST",30);YXb=new CZb(uRc,31);bYb=new CZb(wRc,32);cYb=new CZb(vRc,33);lYb=new CZb(MSc,34);hZb=new CZb(CMc,35);kYb=new CZb(xRc,36);oYb=new CZb(yRc,37);sYb=new CZb("LET",38);JYb=new CZb(zRc,39);RYb=new CZb(ARc,40);SYb=new CZb(BRc,41);TYb=new CZb(CRc,42);fZb=new CZb("STATIC",43);zZb=new CZb("YIELD", +44);FYb=new DZb("OPEN_CURLY",45,"{");KXb=new DZb("CLOSE_CURLY",46,"}");GYb=new DZb("OPEN_PAREN",47,"(");LXb=new DZb("CLOSE_PAREN",48,")");HYb=new DZb("OPEN_SQUARE",49,"[");MXb=new DZb("CLOSE_SQUARE",50,"]");MYb=new DZb("PERIOD",51,".");ZYb=new DZb("SEMI_COLON",52,";");OXb=new DZb("COMMA",53,",");EYb=new DZb("OPEN_ANGLE",54,"<");JXb=new DZb("CLOSE_ANGLE",55,">");rYb=new DZb("LESS_EQUAL",56,"<=");hYb=new DZb("GREATER_EQUAL",57,">=");zXb=new DZb("ARROW",58,"=>");$Xb=new DZb("EQUAL_EQUAL",59,"==");zYb= +new DZb("NOT_EQUAL",60,"!=");_Xb=new DZb("EQUAL_EQUAL_EQUAL",61,"===");AYb=new DZb("NOT_EQUAL_EQUAL",62,"!==");NYb=new DZb("PLUS",63,"+");tYb=new DZb("MINUS",64,"-");bZb=new DZb("STAR",65,"*");dZb=new DZb("STAR_STAR",66,"**");KYb=new DZb("PERCENT",67,"%");PYb=new DZb("PLUS_PLUS",68,"++");vYb=new DZb("MINUS_MINUS",69,"--");pYb=new DZb("LEFT_SHIFT",70,"<<");XYb=new DZb("RIGHT_SHIFT",71,">>");tZb=new DZb("UNSIGNED_RIGHT_SHIFT",72,">>>");wXb=new DZb("AMPERSAND",73,"&");BXb=new DZb("BAR",74,"|");EXb=new DZb("CARET", +75,"^");AXb=new DZb("BANG",76,"!");oZb=new DZb("TILDE",77,"~");yXb=new DZb("AND",78,"&&");IYb=new DZb("OR",79,"||");UYb=new DZb("QUESTION",80,"?");NXb=new DZb("COLON",81,":");ZXb=new DZb("EQUAL",82,"=");OYb=new DZb("PLUS_EQUAL",83,"+=");uYb=new DZb("MINUS_EQUAL",84,"-=");cZb=new DZb("STAR_EQUAL",85,"*=");eZb=new DZb("STAR_STAR_EQUAL",86,"**=");LYb=new DZb("PERCENT_EQUAL",87,"%=");qYb=new DZb("LEFT_SHIFT_EQUAL",88,"<<=");YYb=new DZb("RIGHT_SHIFT_EQUAL",89,">>=");uZb=new DZb("UNSIGNED_RIGHT_SHIFT_EQUAL", +90,">>>=");xXb=new DZb("AMPERSAND_EQUAL",91,"&=");CXb=new DZb("BAR_EQUAL",92,"|=");FXb=new DZb("CARET_EQUAL",93,"^=");$Yb=new DZb("SLASH",94,"/");_Yb=new DZb("SLASH_EQUAL",95,"/=");QYb=new DZb("POUND",96,"#");CYb=new CZb(NSc,97);pZb=new CZb(OSc,98);dYb=new CZb(PSc,99);DYb=new DZb("NUMBER",100,"number literal");gZb=new DZb(fSc,101,"string literal");VYb=new DZb("REGULAR_EXPRESSION",102,"regular expression literal");aZb=new DZb("SPREAD",103,HNc);jZb=new DZb("TEMPLATE_HEAD",104,"template head");kZb=new DZb("TEMPLATE_MIDDLE", +105,"template middle");lZb=new DZb("TEMPLATE_TAIL",106,"template tail");BYb=new DZb("NO_SUBSTITUTION_TEMPLATE",107,"no substitution template");rZb=new CZb(ERc,108);SXb=new CZb(QSc,109);wYb=new CZb("MODULE",110);xYb=new CZb(RSc,111)}function n1b(){n1b=JZ;N0b=new o1b("PROGRAM",0);m0b=new o1b("FUNCTION_DECLARATION",1);G_b=new o1b("BLOCK",2);j1b=new o1b("VARIABLE_STATEMENT",3);h1b=new o1b("VARIABLE_DECLARATION",4);b0b=new o1b("EMPTY_STATEMENT",5);f0b=new o1b("EXPRESSION_STATEMENT",6);r0b=new o1b("IF_STATEMENT", +7);__b=new o1b("DO_WHILE_STATEMENT",8);k1b=new o1b("WHILE_STATEMENT",9);j0b=new o1b("FOR_IN_STATEMENT",10);l0b=new o1b("FOR_STATEMENT",11);i1b=new o1b("VARIABLE_DECLARATION_LIST",12);X_b=new o1b("CONTINUE_STATEMENT",13);H_b=new o1b("BREAK_STATEMENT",14);R0b=new o1b("RETURN_STATEMENT",15);l1b=new o1b("WITH_STATEMENT",16);K_b=new o1b("CASE_CLAUSE",17);Z_b=new o1b("DEFAULT_CLAUSE",18);V0b=new o1b("SWITCH_STATEMENT",19);w0b=new o1b("LABELLED_STATEMENT",20);$0b=new o1b("THROW_STATEMENT",21);L_b=new o1b(FSc, +22);_0b=new o1b("TRY_STATEMENT",23);Y_b=new o1b("DEBUGGER_STATEMENT",24);Z0b=new o1b("THIS_EXPRESSION",25);q0b=new o1b("IDENTIFIER_EXPRESSION",26);x0b=new o1b("LITERAL_EXPRESSION",27);A_b=new o1b("ARRAY_LITERAL_EXPRESSION",28);I0b=new o1b("OBJECT_LITERAL_EXPRESSION",29);O_b=new o1b("COMPREHENSION",30);Q_b=new o1b("COMPREHENSION_IF",31);P_b=new o1b("COMPREHENSION_FOR",32);p0b=new o1b("GET_ACCESSOR",33);S0b=new o1b("SET_ACCESSOR",34);O0b=new o1b("PROPERTY_NAME_ASSIGNMENT",35);R_b=new o1b("COMPUTED_PROPERTY_DEFINITION", +36);S_b=new o1b("COMPUTED_PROPERTY_GETTER",37);U_b=new o1b("COMPUTED_PROPERTY_METHOD",38);V_b=new o1b("COMPUTED_PROPERTY_SETTER",39);B0b=new o1b("MISSING_PRIMARY_EXPRESSION",40);N_b=new o1b("COMMA_EXPRESSION",41);F_b=new o1b("BINARY_OPERATOR",42);W_b=new o1b("CONDITIONAL_EXPRESSION",43);e1b=new o1b("UNARY_EXPRESSION",44);g1b=new o1b("UPDATE_EXPRESSION",45);y0b=new o1b("MEMBER_EXPRESSION",46);F0b=new o1b("NEW_EXPRESSION",47);z_b=new o1b("ARGUMENT_LIST",48);I_b=new o1b("CALL_EXPRESSION",49);M_b=new o1b("CLASS_DECLARATION", +50);v0b=new o1b("INTERFACE_DECLARATION",51);D0b=new o1b(CSc,52);E0b=new o1b("NAMESPACE_NAME",53);c0b=new o1b("ENUM_DECLARATION",54);z0b=new o1b("MEMBER_LOOKUP_EXPRESSION",55);M0b=new o1b("PAREN_EXPRESSION",56);g0b=new o1b(JSc,57);U0b=new o1b("SUPER_EXPRESSION",58);B_b=new o1b(dTc,59);D_b=new o1b("ASSIGNMENT_REST_ELEMENT",60);J0b=new o1b(eTc,61);h0b=new o1b("FORMAL_PARAMETER_LIST",62);T0b=new o1b("SPREAD_EXPRESSION",63);H0b=new o1b(NSc,64);Q0b=new o1b("REST_PARAMETER",65);C0b=new o1b("MODULE_IMPORT", +66);d0b=new o1b("EXPORT_DECLARATION",67);e0b=new o1b("EXPORT_SPECIFIER",68);s0b=new o1b("IMPORT_DECLARATION",69);t0b=new o1b("IMPORT_SPECIFIER",70);k0b=new o1b("FOR_OF_STATEMENT",71);i0b=new o1b("FOR_AWAIT_OF_STATEMENT",72);m1b=new o1b("YIELD_EXPRESSION",73);$_b=new o1b("DEFAULT_PARAMETER",74);W0b=new o1b("TEMPLATE_LITERAL_EXPRESSION",75);X0b=new o1b("TEMPLATE_LITERAL_PORTION",76);Y0b=new o1b("TEMPLATE_SUBSTITUTION",77);c1b=new o1b("TYPE_NAME",78);d1b=new o1b("TYPE_QUERY",79);a1b=new o1b("TYPED_PARAMETER", +80);K0b=new o1b(DSc,81);L0b=new o1b("PARAMETERIZED_TYPE_TREE",82);C_b=new o1b(fTc,83);P0b=new o1b(gTc,84);f1b=new o1b(hTc,85);n0b=new o1b(iTc,86);o0b=new o1b(jTc,87);A0b=new o1b("MEMBER_VARIABLE",88);T_b=new o1b("COMPUTED_PROPERTY_MEMBER_VARIABLE",89);b1b=new o1b(ESc,90);y_b=new o1b(zSc,91);u0b=new o1b(BSc,92);J_b=new o1b(ASc,93);G0b=new o1b("NEW_TARGET_EXPRESSION",94);E_b=new o1b("AWAIT_EXPRESSION",95);a0b=new o1b("DYNAMIC_IMPORT_EXPRESSION",96)}function MWb(a){var b,c;QWb(a);b=a.f;if(!xWb(a,a.f))return tWb(a, +(AZb(),XXb),b);c=yWb(a);switch(c){case 123:return tWb(a,(AZb(),FYb),b);case 125:return tWb(a,(AZb(),KXb),b);case 40:return tWb(a,(AZb(),GYb),b);case 41:return tWb(a,(AZb(),LXb),b);case 91:return tWb(a,(AZb(),HYb),b);case 93:return tWb(a,(AZb(),MXb),b);case 46:if(gXb(xWb(a,a.f)?zsc(a.b,a.f):0))return RWb(a),HWb(a,b);if((xWb(a,a.f)?zsc(a.b,a.f):0)==46&&(xWb(a,a.f+1)?zsc(a.b,a.f+1):0)==46){yWb(a);yWb(a);return tWb(a,(AZb(),aZb),b)}return tWb(a,(AZb(),MYb),b);case 59:return tWb(a,(AZb(),ZYb),b);case 44:return tWb(a, +(AZb(),OXb),b);case 126:return tWb(a,(AZb(),oZb),b);case 63:return tWb(a,(AZb(),UYb),b);case 58:return tWb(a,(AZb(),NXb),b);case 60:switch(xWb(a,a.f)?zsc(a.b,a.f):0){case 60:yWb(a);if((xWb(a,a.f)?zsc(a.b,a.f):0)==61){yWb(a);return tWb(a,(AZb(),qYb),b)}return tWb(a,(AZb(),pYb),b);case 61:yWb(a);return tWb(a,(AZb(),rYb),b);default:return tWb(a,(AZb(),EYb),b)}case 62:if(a.j>0)return tWb(a,(AZb(),JXb),b);switch(xWb(a,a.f)?zsc(a.b,a.f):0){case 62:yWb(a);switch(xWb(a,a.f)?zsc(a.b,a.f):0){case 61:yWb(a); +return tWb(a,(AZb(),YYb),b);case 62:yWb(a);if((xWb(a,a.f)?zsc(a.b,a.f):0)==61){yWb(a);return tWb(a,(AZb(),uZb),b)}return tWb(a,(AZb(),tZb),b);default:return tWb(a,(AZb(),XYb),b)}case 61:yWb(a);return tWb(a,(AZb(),hYb),b);default:return tWb(a,(AZb(),JXb),b)}case 61:switch(xWb(a,a.f)?zsc(a.b,a.f):0){case 61:yWb(a);if((xWb(a,a.f)?zsc(a.b,a.f):0)==61){yWb(a);return tWb(a,(AZb(),_Xb),b)}return tWb(a,(AZb(),$Xb),b);case 62:yWb(a);return tWb(a,(AZb(),zXb),b);default:return tWb(a,(AZb(),ZXb),b)}case 33:if((xWb(a, +a.f)?zsc(a.b,a.f):0)==61){yWb(a);if((xWb(a,a.f)?zsc(a.b,a.f):0)==61){yWb(a);return tWb(a,(AZb(),AYb),b)}return tWb(a,(AZb(),zYb),b)}return tWb(a,(AZb(),AXb),b);case 42:if((xWb(a,a.f)?zsc(a.b,a.f):0)==61){yWb(a);return tWb(a,(AZb(),cZb),b)}else if((xWb(a,a.f)?zsc(a.b,a.f):0)==42){yWb(a);if((xWb(a,a.f)?zsc(a.b,a.f):0)==61){yWb(a);return tWb(a,(AZb(),eZb),b)}else return tWb(a,(AZb(),dZb),b)}return tWb(a,(AZb(),bZb),b);case 37:if((xWb(a,a.f)?zsc(a.b,a.f):0)==61){yWb(a);return tWb(a,(AZb(),LYb),b)}return tWb(a, +(AZb(),KYb),b);case 94:if((xWb(a,a.f)?zsc(a.b,a.f):0)==61){yWb(a);return tWb(a,(AZb(),FXb),b)}return tWb(a,(AZb(),EXb),b);case 47:if((xWb(a,a.f)?zsc(a.b,a.f):0)==61){yWb(a);return tWb(a,(AZb(),_Yb),b)}return tWb(a,(AZb(),$Yb),b);case 43:switch(xWb(a,a.f)?zsc(a.b,a.f):0){case 43:yWb(a);return tWb(a,(AZb(),PYb),b);case 61:yWb(a);return tWb(a,(AZb(),OYb),b);default:return tWb(a,(AZb(),NYb),b)}case 45:switch(xWb(a,a.f)?zsc(a.b,a.f):0){case 45:yWb(a);return tWb(a,(AZb(),vYb),b);case 61:yWb(a);return tWb(a, +(AZb(),uYb),b);default:return tWb(a,(AZb(),tYb),b)}case 38:switch(xWb(a,a.f)?zsc(a.b,a.f):0){case 38:yWb(a);return tWb(a,(AZb(),yXb),b);case 61:yWb(a);return tWb(a,(AZb(),xXb),b);default:return tWb(a,(AZb(),wXb),b)}case 124:switch(xWb(a,a.f)?zsc(a.b,a.f):0){case 124:yWb(a);return tWb(a,(AZb(),IYb),b);case 61:yWb(a);return tWb(a,(AZb(),CXb),b);default:return tWb(a,(AZb(),BXb),b)}case 35:return tWb(a,(AZb(),QYb),b);case 48:return KWb(a,b);case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:return RWb(a), +IWb(a,b);case 34:case 39:return LWb(a,b,c);case 96:return!xWb(a,a.f)&&EWb(a,ZSb(a.i.b,b),$Sc,CE(xE(iW,1),mLc,1,5,[])),BWb(a,(AZb(),BYb),jZb);default:return JWb(a,b,c)}}function Afc(){Afc=JZ;Uec=new Bfc(DRc,0);Tcc=new Bfc("BITOR",1);Ucc=new Bfc("BITXOR",2);Rcc=new Bfc("BITAND",3);xdc=new Bfc("EQ",4);vec=new Bfc("NE",5);lec=new Bfc("LT",6);iec=new Bfc("LE",7);Rdc=new Bfc("GT",8);Ldc=new Bfc("GE",9);kec=new Bfc("LSH",10);Wec=new Bfc("RSH",11);tfc=new Bfc("URSH",12);vcc=new Bfc("ADD",13);efc=new Bfc("SUB", +14);qec=new Bfc("MUL",15);pdc=new Bfc("DIV",16);oec=new Bfc("MOD",17);zdc=new Bfc("EXPONENT",18);zec=new Bfc("NOT",19);Scc=new Bfc("BITNOT",20);Oec=new Bfc("POS",21);wec=new Bfc("NEG",22);xec=new Bfc("NEW",23);ndc=new Bfc("DELPROP",24);pfc=new Bfc(LSc,25);Pdc=new Bfc("GETPROP",26);Odc=new Bfc("GETELEM",27);Ycc=new Bfc("CALL",28);rec=new Bfc("NAME",29);Cec=new Bfc("NUMBER",30);bfc=new Bfc(fSc,31);Aec=new Bfc(NSc,32);lfc=new Bfc(EMc,33);Edc=new Bfc(PSc,34);nfc=new Bfc(OSc,35);Zec=new Bfc("SHEQ",36); +$ec=new Bfc("SHNE",37);Rec=new Bfc("REGEXP",38);mfc=new Bfc("THROW",39);Zdc=new Bfc("IN",40);aec=new Bfc(uSc,41);zcc=new Bfc("ARRAYLIT",42);Eec=new Bfc("OBJECTLIT",43);ofc=new Bfc("TRY",44);Jec=new Bfc("PARAM_LIST",45);edc=new Bfc("COMMA",46);Ccc=new Bfc("ASSIGN",47);Fcc=new Bfc("ASSIGN_BITOR",48);Gcc=new Bfc("ASSIGN_BITXOR",49);Ecc=new Bfc("ASSIGN_BITAND",50);Jcc=new Bfc("ASSIGN_LSH",51);Mcc=new Bfc("ASSIGN_RSH",52);Occ=new Bfc("ASSIGN_URSH",53);Dcc=new Bfc("ASSIGN_ADD",54);Ncc=new Bfc("ASSIGN_SUB", +55);Lcc=new Bfc("ASSIGN_MUL",56);Hcc=new Bfc("ASSIGN_DIV",57);Kcc=new Bfc("ASSIGN_MOD",58);Icc=new Bfc("ASSIGN_EXPONENT",59);Sdc=new Bfc("HOOK",60);Hec=new Bfc("OR",61);wcc=new Bfc("AND",62);$dc=new Bfc("INC",63);jdc=new Bfc("DEC",64);Jdc=new Bfc(jQc,65);Tdc=new Bfc("IF",66);gfc=new Bfc(KSc,67);$cc=new Bfc("CASE",68);ldc=new Bfc("DEFAULT_CASE",69);xfc=new Bfc("WHILE",70);qdc=new Bfc("DO",71);Fdc=new Bfc("FOR",72);Hdc=new Bfc("FOR_IN",73);Xcc=new Bfc("BREAK",74);hdc=new Bfc(GSc,75);ufc=new Bfc("VAR", +76);yfc=new Bfc("WITH",77);adc=new Bfc(FSc,78);vfc=new Bfc("VOID",79);tdc=new Bfc("EMPTY",80);Vec=new Bfc("ROOT",81);Vcc=new Bfc("BLOCK",82);eec=new Bfc("LABEL",83);Ddc=new Bfc("EXPR_RESULT",84);Xec=new Bfc("SCRIPT",85);Qdc=new Bfc("GETTER_DEF",86);Yec=new Bfc("SETTER_DEF",87);gdc=new Bfc("CONST",88);idc=new Bfc(HSc,89);fec=new Bfc("LABEL_NAME",90);cfc=new Bfc("STRING_KEY",91);_cc=new Bfc("CAST",92);Acc=new Bfc(dTc,93);Fec=new Bfc(eTc,94);odc=new Bfc("DESTRUCTURING_LHS",95);bdc=new Bfc(iQc,96);cdc= +new Bfc("CLASS_MEMBERS",97);mec=new Bfc("MEMBER_FUNCTION_DEF",98);ffc=new Bfc(CMc,99);jec=new Bfc("LET",100);Idc=new Bfc("FOR_OF",101);Gdc=new Bfc(xSc,102);zfc=new Bfc("YIELD",103);Pcc=new Bfc("AWAIT",104);Vdc=new Bfc(MSc,105);Xdc=new Bfc("IMPORT_SPECS",106);Wdc=new Bfc("IMPORT_SPEC",107);Ydc=new Bfc("IMPORT_STAR",108);Adc=new Bfc(wRc,109);Cdc=new Bfc("EXPORT_SPECS",110);Bdc=new Bfc("EXPORT_SPEC",111);pec=new Bfc("MODULE_BODY",112);rdc=new Bfc(ySc,113);Sec=new Bfc("REST",114);_ec=new Bfc("SPREAD", +115);fdc=new Bfc("COMPUTED_PROP",116);hfc=new Bfc("TAGGED_TEMPLATELIT",117);ifc=new Bfc("TEMPLATELIT",118);kfc=new Bfc("TEMPLATELIT_SUB",119);jfc=new Bfc("TEMPLATELIT_STRING",120);mdc=new Bfc("DEFAULT_VALUE",121);yec=new Bfc(wSc,122);dfc=new Bfc("STRING_TYPE",123);Wcc=new Bfc("BOOLEAN_TYPE",124);Dec=new Bfc("NUMBER_TYPE",125);Kdc=new Bfc(iTc,126);Iec=new Bfc("PARAMETERIZED_TYPE",127);sfc=new Bfc(hTc,128);ycc=new Bfc("ANY_TYPE",129);Bec=new Bfc("NULLABLE_TYPE",130);wfc=new Bfc("VOID_TYPE",131);Tec= +new Bfc("REST_PARAMETER_TYPE",132);sec=new Bfc("NAMED_TYPE",133);Gec=new Bfc(DSc,134);Qec=new Bfc(gTc,135);rfc=new Bfc("UNDEFINED_TYPE",136);Bcc=new Bfc(fTc,137);Mdc=new Bfc(wTc,138);Ndc=new Bfc(jTc,139);xcc=new Bfc(gSc,140);Kec=new Bfc("PIPE",141);afc=new Bfc("STAR",142);wdc=new Bfc("EOC",143);Pec=new Bfc("QMARK",144);sdc=new Bfc("ELLIPSIS",145);Qcc=new Bfc("BANG",146);ydc=new Bfc("EQUALS",147);gec=new Bfc("LB",148);hec=new Bfc("LC",149);ddc=new Bfc("COLON",150);bec=new Bfc(yRc,151);cec=new Bfc("INTERFACE_EXTENDS", +152);dec=new Bfc("INTERFACE_MEMBERS",153);udc=new Bfc(uRc,154);vdc=new Bfc("ENUM_MEMBERS",155);Udc=new Bfc(xRc,156);qfc=new Bfc(ESc,157);kdc=new Bfc(QSc,158);nec=new Bfc("MEMBER_VARIABLE_DEF",159);_dc=new Bfc(BSc,160);Zcc=new Bfc(ASc,161);tec=new Bfc(RSc,162);uec=new Bfc("NAMESPACE_ELEMENTS",163);Lec=new Bfc("PLACEHOLDER1",164);Mec=new Bfc("PLACEHOLDER2",165);Nec=new Bfc("PLACEHOLDER3",166)}function yBb(){yBb=JZ;new Mcb("JSC_INTERNAL_ERROR_UNEXPECTED_TOKEN",(E2(),B2),new Rtc("Internal Error: TypeCheck doesn''t know how to handle {0}")); +TAb=new Mcb("JSC_DETERMINISTIC_TEST",D2,new Rtc("condition always evaluates to {2}\nleft : {0}\nright: {1}"));eBb=new Mcb("JSC_INEXISTENT_ENUM_ELEMENT",D2,new Rtc("element {0} does not exist on this enum"));fBb=new Mcb("JSC_INEXISTENT_PROPERTY",D2,new Rtc(jRc));qBb=new Mcb("JSC_POSSIBLE_INEXISTENT_PROPERTY",C2,new Rtc(jRc));gBb=new Mcb("JSC_INEXISTENT_PROPERTY_WITH_SUGGESTION",D2,new Rtc(kRc));rBb=new Mcb("JSC_STRICT_INEXISTENT_PROPERTY",C2,new Rtc(jRc));tBb=new Mcb("JSC_STRICT_INEXISTENT_UNION_PROPERTY", +C2,new Rtc("Property {0} not defined on all member types of {1}"));sBb=new Mcb("JSC_STRICT_INEXISTENT_PROPERTY_WITH_SUGGESTION",C2,new Rtc(kRc));oBb=new Mcb("JSC_NOT_A_CONSTRUCTOR",D2,new Rtc("cannot instantiate non-constructor"));hBb=new Mcb("JSC_INSTANTIATE_ABSTRACT_CLASS",D2,new Rtc("cannot instantiate abstract class"));PAb=new Mcb("JSC_BAD_TYPE_FOR_BIT_OPERATION",D2,new Rtc("operator {0} cannot be applied to {1}"));pBb=new Mcb("JSC_NOT_FUNCTION_TYPE",D2,new Rtc("{0} expressions are not callable")); +SAb=new Mcb("JSC_CONSTRUCTOR_NOT_CALLABLE",D2,new Rtc('Constructor {0} should be called with the "new" keyword'));MAb=new Mcb("JSC_ABSTRACT_SUPER_METHOD_NOT_USABLE",D2,new Rtc("Abstract super method {0} cannot be dereferenced"));XAb=new Mcb("JSC_FUNCTION_MASKS_VARIABLE",D2,new Rtc("function {0} masks variable (IE bug)"));mBb=new Mcb("JSC_MULTIPLE_VAR_DEF",D2,new Rtc("declaration of multiple variables with shared type information"));UAb=new Mcb("JSC_ENUM_DUP",B2,new Rtc("enum element {0} already defined")); +kBb=new Mcb("JSC_INVALID_INTERFACE_MEMBER_DECLARATION",D2,new Rtc("interface members can only be empty property declarations, empty functions{0}"));iBb=new Mcb("JSC_INTERFACE_METHOD_NOT_EMPTY",D2,new Rtc("interface member functions must have an empty body"));QAb=new Mcb("JSC_CONFLICTING_EXTENDED_TYPE",D2,new Rtc("{1} cannot extend this type; {0}s can only extend {0}s"));VAb=new Mcb("JSC_ES5_CLASS_EXTENDING_ES6_CLASS",D2,new Rtc("ES5 class {0} cannot extend ES6 class {1}"));new Mcb("JSC_INTERFACE_EXTENDS_LOOP", +D2,new Rtc("extends loop involving {0}, loop: {1}"));RAb=new Mcb("JSC_CONFLICTING_IMPLEMENTED_TYPE",D2,new Rtc("{0} cannot implement this type; an interface can only extend, but not implement interfaces"));OAb=new Mcb("JSC_IMPLEMENTS_NON_INTERFACE",D2,new Rtc("can only implement interfaces"));ZAb=new Mcb("JSC_HIDDEN_SUPERCLASS_PROPERTY",C2,new Rtc("property {0} already defined on superclass {1}; use @override to override it"));YAb=new Mcb("JSC_HIDDEN_INTERFACE_PROPERTY",C2,new Rtc("property {0} already defined on interface {1}; use @override to override it")); +$Ab=new Mcb("JSC_HIDDEN_SUPERCLASS_PROPERTY_MISMATCH",D2,new Rtc("mismatch of the {0} property type and the type of the property it overrides from superclass {1}\noriginal: {2}\noverride: {3}"));vBb=new Mcb("JSC_UNKNOWN_OVERRIDE",D2,new Rtc("property {0} not defined on any superclass of {1}"));jBb=new Mcb("JSC_INTERFACE_METHOD_OVERRIDE",D2,new Rtc("property {0} is already defined by the {1} extended interface"));uBb=new Mcb("JSC_UNKNOWN_EXPR_TYPE",D2,new Rtc("could not determine the type of this expression")); +wBb=new Mcb("JSC_UNRESOLVED_TYPE",D2,new Rtc("could not resolve the name {0} to a type"));xBb=new Mcb("JSC_WRONG_ARGUMENT_COUNT",D2,new Rtc("Function {0}: called with {1} argument(s). Function requires at least {2} argument(s){3}."));aBb=new Mcb("JSC_ILLEGAL_IMPLICIT_CAST",D2,new Rtc("Illegal annotation on {0}. @implicitCast may only be used in externs."));dBb=new Mcb("JSC_INCOMPATIBLE_EXTENDED_PROPERTY_TYPE",D2,new Rtc("Interface {0} has a property {1} with incompatible types in its super interfaces {2} and {3}")); +WAb=new Mcb("JSC_EXPECTED_THIS_TYPE",D2,new Rtc('"{0}" must be called with a "this" type'));lBb=new Mcb("JSC_IN_USED_WITH_STRUCT",D2,new Rtc("Cannot use the IN operator with structs"));cBb=new Mcb("JSC_ILLEGAL_PROPERTY_CREATION",D2,new Rtc("Cannot add a property to a struct instance after it is constructed. (If you already declared the property, make sure to give it a type.)"));bBb=new Mcb("JSC_ILLEGAL_OBJLIT_KEY",D2,new Rtc("Illegal key, the object literal is a {0}"));_Ab=new Mcb("JSC_ILLEGAL_CLASS_KEY", +D2,new Rtc("Illegal key, the class is a {0}"));nBb=new Mcb("JSC_NON_STRINGIFIABLE_OBJECT_KEY",D2,new Rtc('Object type "{0}" contains non-stringifiable key and it may lead to an error. Please use ES6 Map instead or implement your own Map structure.'));LAb=new Mcb("JSC_ABSTRACT_METHOD_IN_CONCRETE_CLASS",D2,new Rtc("Abstract methods can only appear in abstract classes. Please declare the class as @abstract"));NAb=new bcb(CE(xE(bM,1),mLc,5,0,[TAb,eBb,fBb,qBb,gBb,oBb,hBb,PAb,pBb,SAb,XAb,mBb,UAb,kBb,iBb, +QAb,RAb,OAb,$Ab,vBb,jBb,wBb,xBb,aBb,dBb,WAb,lBb,_Ab,cBb,bBb,nBb,LAb,MAb,VAb,(kwb(),gwb),iwb,(oCb(),nCb),lCb,fCb,jCb,($kb(),Xkb)]))}function Hcb(){Hcb=JZ;Dcb=new Mcb("JSC_UNUSED",(E2(),D2),new Rtc(GOc));tp(HOc,IOc,JOc,KOc,LOc,MOc,CE(xE(nW,1),uNc,2,6,[NOc,OOc,POc,QOc,ROc]));Gcb=new fDc;Kcb("untranspilableFeatures",CE(xE(bM,1),mLc,5,0,[(Hob(),Gob)]));Kcb("featuresNotSupportedByPass",CE(xE(bM,1),mLc,5,0,[(Msb(),Jsb)]));Kcb("moduleLoad",CE(xE(bM,1),mLc,5,0,[(ZEb(),YEb),(KHb(),JHb),(Itb(),Gtb),Htb]));scb= +Kcb("globalThis",CE(xE(bM,1),mLc,5,0,[(p2(),o2)]));Kcb(SOc,CE(xE(bM,1),mLc,5,0,[(d2(),Y1),Z1,$1,_1,W1,X1]));Kcb("underscore",CE(xE(bM,1),mLc,5,0,[(jHb(),eHb),fHb]));Fcb=Kcb("visibility",CE(xE(bM,1),mLc,5,0,[Q1,R1,P1,S1,a2,b2,c2,V1]));Icb("accessControls",Fcb);Kcb("nonStandardJsDocs",CE(xE(bM,1),mLc,5,0,[(kwb(),Tvb),Yvb,(A2(),w2)]));Kcb("invalidCasts",CE(xE(bM,1),mLc,5,0,[(MBb(),HBb)]));Icb(TOc,new _bb(TOc,CE(xE(bM,1),mLc,5,0,[Dcb])));Kcb("strictModuleDepCheck",CE(xE(bM,1),mLc,5,0,[(ICb(),CCb),(n2(), +l2)]));Kcb("violatedModuleDep",CE(xE(bM,1),mLc,5,0,[HCb]));Kcb("externsValidation",CE(xE(bM,1),mLc,5,0,[ACb,DCb]));Kcb("ambiguousFunctionDecl",CE(xE(bM,1),mLc,5,0,[(Pxb(),Hxb)]));Kcb("unknownDefines",CE(xE(bM,1),mLc,5,0,[(Qtb(),Ptb)]));Kcb("tweakValidation",CE(xE(bM,1),mLc,5,0,[(Vtb(),Rtb),Ttb,Utb]));Kcb("missingOverride",CE(xE(bM,1),mLc,5,0,[(yBb(),YAb),ZAb]));ucb=Kcb(UOc,CE(xE(bM,1),mLc,5,0,[fBb,gBb,qBb]));Kcb("globallyMissingProperties",CE(xE(bM,1),mLc,5,0,[qBb]));Kcb("j2clChecks",CE(xE(bM,1), +mLc,5,0,[(wmb(),vmb)]));Kcb("missingReturn",CE(xE(bM,1),mLc,5,0,[(N2(),M2)]));Kcb("internetExplorerChecks",CE(xE(bM,1),mLc,5,0,[fwb]));Ccb=Kcb("undefinedVars",CE(xE(bM,1),mLc,5,0,[ECb]));Bcb=Kcb("undefinedNames",CE(xE(bM,1),mLc,5,0,[m2]));Kcb("checkDebuggerStatement",CE(xE(bM,1),mLc,5,0,[(k2(),j2)]));Kcb("checkRegExp",CE(xE(bM,1),mLc,5,0,[(S2(),R2),Q2]));mcb=Jcb(sOc,CE(xE(aM,1),mLc,53,0,[ABb,NAb,($kb(),Lkb),scb]));Icb(MOc,new _bb(MOc,CE(xE(bM,1),mLc,5,0,[Dcb])));Icb(NOc,new _bb(NOc,CE(xE(bM,1),mLc, +5,0,[Dcb])));Icb(LOc,new _bb(LOc,CE(xE(bM,1),mLc,5,0,[Dcb])));Icb(VOc,new _bb(VOc,CE(xE(bM,1),mLc,5,0,[Dcb])));Kcb("tooManyTypeParams",CE(xE(bM,1),mLc,5,0,[ewb]));Icb(WOc,new _bb(WOc,CE(xE(bM,1),mLc,5,0,[Dcb])));Kcb(KOc,CE(xE(bM,1),mLc,5,0,[uBb]));ycb=Kcb(POc,CE(xE(bM,1),mLc,5,0,[rBb,sBb,tBb]));Acb=Kcb(QOc,CE(xE(bM,1),mLc,5,0,[IBb]));xcb=Jcb(ROc,CE(xE(aM,1),mLc,53,0,[ycb,Acb]));wcb=Kcb(HOc,CE(xE(bM,1),mLc,5,0,[uBb]));ncb=Kcb("checkVars",CE(xE(bM,1),mLc,5,0,[ECb,GCb,(aDb(),ZCb),$Cb]));Kcb("uselessCode", +CE(xE(bM,1),mLc,5,0,[(U2(),T2),(e3(),d3)]));Kcb(XOc,CE(xE(bM,1),mLc,5,0,[T1,U1,(Bbb(),Abb)]));Kcb("accessControlsConst",CE(xE(bM,1),mLc,5,0,[T1,U1]));Kcb("constantProperty",CE(xE(bM,1),mLc,5,0,[T1,U1]));Kcb("typeInvalidation",CE(xE(bM,1),mLc,5,0,[(Scb(),Qcb),Rcb]));ocb=Kcb(YOc,CE(xE(bM,1),mLc,5,0,[(m6(),l6),GCb,BBb,CBb,XAb,$Cb]));Kcb("es3",CE(xE(bM,1),mLc,5,0,[Wvb,fwb]));rcb=Kcb("es5StrictUncommon",CE(xE(bM,1),mLc,5,0,[Xvb,Uvb,Oxb,Lxb,Kxb,Gxb,Dxb,Ixb,Jxb]));qcb=Kcb("es5StrictReflection",CE(xE(bM, +1),mLc,5,0,[Exb,Fxb,Nxb,Mxb]));pcb=Jcb("es5Strict",CE(xE(aM,1),mLc,53,0,[rcb,qcb]));vcb=Kcb("missingProvide",CE(xE(bM,1),mLc,5,0,[(P2(),O2),(H3(),G3)]));Kcb("unrecognizedTypeError",CE(xE(bM,1),mLc,5,0,[iwb]));Kcb(ZOc,CE(xE(bM,1),mLc,5,0,[(L2(),K2)]));Jcb(OOc,CE(xE(aM,1),mLc,53,0,[wcb,Bcb,Ccb,vcb,ccb(Skb),ccb((Ftb(),Dtb)),ucb,ocb,ccb(Otb),ccb((Rdb(),Qdb)),ccb(YEb)]));zcb=Kcb("strictMissingRequire",CE(xE(bM,1),mLc,5,0,[K2,I2,J2]));Kcb("legacyGoogScopeRequire",CE(xE(bM,1),mLc,5,0,[I2,H2]));Kcb("extraRequire", +CE(xE(bM,1),mLc,5,0,[H2]));Kcb("misplacedTypeAnnotation",CE(xE(bM,1),mLc,5,0,[r2,s2,t2,v2,u2,x2,y2]));Kcb("misplacedMsgAnnotation",CE(xE(bM,1),mLc,5,0,[y2]));Kcb("misplacedSuppress",CE(xE(bM,1),mLc,5,0,[z2]));Kcb("suspiciousCode",CE(xE(bM,1),mLc,5,0,[(IGb(),HGb),(_2(),$2),V2,X2,W2,Y2,Z2,TAb,Gtb]));Kcb("functionParams",CE(xE(bM,1),mLc,5,0,[Qkb,Rkb]));Kcb("deprecatedAnnotations",CE(xE(bM,1),mLc,5,0,[q2]));Ecb=Kcb("unusedPrivateMembers",CE(xE(bM,1),mLc,5,0,[(g3(),f3)]));Kcb("unusedLocalVariables",CE(xE(bM, +1),mLc,5,0,[_Cb]));tcb=Kcb("jsdocMissingConst",CE(xE(bM,1),mLc,5,0,[(i2(),h2)]));Kcb("jsdocMissingType",CE(xE(bM,1),mLc,5,0,[$vb]));Kcb("unnecessaryEscape",CE(xE(bM,1),mLc,5,0,[hwb]));Kcb("typeImportCodeReferences",CE(xE(bM,1),mLc,5,0,[(b3(),a3)]));Jcb("lintChecks",CE(xE(aM,1),mLc,53,0,[YGb,new bcb(CE(xE(bM,1),mLc,5,0,[(f2(),e2),(KGb(),JGb),(PGb(),LGb),MGb,NGb,OGb,(RGb(),QGb),(UGb(),SGb),TGb,(nHb(),mHb),(XGb(),VGb),WGb,(lHb(),kHb),(rHb(),oHb),pHb,qHb,(xHb(),vHb),wHb,(zHb(),yHb),(EHb(),AHb),DHb,CHb, +BHb,(GHb(),FHb),(IHb(),HHb),(r3(),i3),k3,l3,o3,m3,n3,p3,q3,(K3(),J3),Zvb]))]));Kcb("strictModuleChecks",CE(xE(bM,1),mLc,5,0,[h3,o3,m3,n3,p3,q3]));lcb=Kcb(JOc,CE(xE(bM,1),mLc,5,0,[(GGb(),FGb),(uHb(),sHb),tHb,(umb(),tmb)]));Jcb(IOc,CE(xE(aM,1),mLc,53,0,[lcb,Ecb,tcb]));Kcb("useOfGoogBase",CE(xE(bM,1),mLc,5,0,[Etb]));Kcb("closureDepMethodUsageChecks",CE(xE(bM,1),mLc,5,0,[F3,ztb,Atb]));Kcb("transitionalSuspiciousCodeWarnings",CE(xE(bM,1),mLc,5,0,[(osb(),msb),nsb,lsb]));Kcb("lateProvide",CE(xE(bM,1),mLc, +5,0,[Ctb]));Kcb("missingPolyfill",CE(xE(bM,1),mLc,5,0,[(Avb(),zvb)]));Kcb("polymer",CE(xE(bM,1),mLc,5,0,[(ntb(),ltb)]));Kcb($Oc,CE(xE(bM,1),mLc,5,0,[mtb]));Kcb("invalidProvide",CE(xE(bM,1),mLc,5,0,[Btb]));Kcb("es6Typed",CE(xE(bM,1),mLc,5,0,[awb]));Icb(_Oc,new _bb(_Oc,CE(xE(bM,1),mLc,5,0,[Dcb])));Kcb("conflictingIjsFile",CE(xE(bM,1),mLc,5,0,[(EGb(),DGb)]));Kcb("googModuleExportNotAStatement",CE(xE(bM,1),mLc,5,0,[j3]))}function clc(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,A,B,C,D,F,G,H,I, +J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,$,ab,bb,cb,db,eb,fb,gb,hb,ib,jb,kb,lb,mb,nb,ob,pb,qb,rb,sb,tb,ub;j=new thc(a);a.u[8]=j;L=new Mmc(a);a.u[32]=L;U=new Smc(a);a.u[33]=U;jb=new Anc(a);a.u[45]=jb;nb=new Enc(a);a.u[48]=nb;tb=new _pc(a,false);a.u[51]=tb;k=new _pc(a,true);a.u[11]=k;ub=new mqc(a);a.u[52]=ub;b=new Wgc(a);a.u[57]=b;a.n=new Inc(a,"IObject#KEY1");a.k=new Inc(a,"IObject#VALUE");a.a=new Inc(a,"T");a.s=new Inc(a,DTc);a.p=new Inc(a,DTc);a.d=new Inc(a,DTc);a.i=new Inc(a,DTc);a.b=new Inc(a,DTc);a.r= +new Inc(a,DTc);a.c=new Inc(a,DTc);a.o=new Inc(a,ERc);a.w=new Inc(a,ERc);qb=new Wic(a,null,null,true,null);a.u[53]=qb;t=elc(a,"IObject",CE(xE(vV,1),xPc,62,0,[a.n,a.k]));a.u[28]=t;ilc(a,29,(ae(t.f==(dkc(),akc)||t.f==bkc,lRc,t),cgc(t.k)));W=uic(Iic(Fic(Bic(Aic(vic(xic(new Lic(a))),NMc),Tkc(a,CE(xE(cV,1),xPc,23,0,[b])))),CE(xE(vV,1),xPc,62,0,[a.n,a.k])));Gjc(W,qb,null);a.u[39]=W;ilc(a,40,zjc(W));ab=(ae(W.f==akc||W.f==bkc,lRc,W),cgc(W.k));a.u[38]=ab;n=uic(Cic(Dic(Bic(Aic(vic(xic(new Lic(a))),nPc),Vkc(a, +true,CE(xE(cV,1),xPc,23,0,[b]))),tb),ab));Hjc(n,ab,null);a.u[16]=n;o=zjc(n);a.u[18]=o;K=new vmc(a);a.u[58]=K;I=new imc(a);a.u[59]=I;J=new Fmc(a);a.u[60]=J;A=elc(a,"Iterable",CE(xE(vV,1),xPc,62,0,[a.r]));a.u[23]=A;B=(ae(A.f==akc||A.f==bkc,lRc,A),cgc(A.k));a.u[24]=B;C=elc(a,"Iterator",CE(xE(vV,1),xPc,62,0,[a.s]));a.u[25]=C;D=(ae(C.f==akc||C.f==bkc,lRc,C),cgc(C.k));a.u[26]=D;v=elc(a,"IIterableResult",CE(xE(vV,1),xPc,62,0,[a.p]));a.u[21]=v;w=(ae(v.f==akc||v.f==bkc,lRc,v),cgc(v.k));a.u[22]=w;c=uic(Jic(Fic(Bic(Aic(vic(xic(new Lic(a))), +kPc),Vkc(a,true,CE(xE(cV,1),xPc,23,0,[b])))),new $nc(a,(Bn(),new hz(Qn(CE(xE(iW,1),mLc,1,5,[a.k,a.a])))),new Wz(Sd(a.a)))));zjc(c);Ejc(c,new Wz(Sd(Ykc(a,B,CE(xE(cV,1),xPc,23,0,[a.a])))));a.u[1]=c;d=(ae(c.f==akc||c.f==bkc,lRc,c),cgc(c.k));a.u[0]=d;u=uic(Bic(Aic(vic(xic(new Lic(a))),"ITemplateArray"),new J9b((Afc(),Jec))));ilc(a,27,(ae(u.f==akc||u.f==bkc,lRc,u),cgc(u.k)));p=elc(a,"Generator",CE(xE(vV,1),xPc,62,0,[a.i]));Djc(p,new hz(Qn(CE(xE(iW,1),mLc,1,5,[Ykc(a,B,CE(xE(cV,1),xPc,23,0,[a.i])),Ykc(a, +D,CE(xE(cV,1),xPc,23,0,[a.i]))]))));a.u[19]=p;ilc(a,20,(ae(p.f==akc||p.f==bkc,lRc,p),cgc(p.k)));g=elc(a,"AsyncIterator",CE(xE(vV,1),xPc,62,0,[a.d]));a.u[4]=g;ilc(a,5,(ae(g.f==akc||g.f==bkc,lRc,g),cgc(g.k)));f=elc(a,"AsyncIterable",CE(xE(vV,1),xPc,62,0,[a.c]));a.u[2]=f;ilc(a,3,(ae(f.f==akc||f.f==bkc,lRc,f),cgc(f.k)));e=elc(a,"AsyncGenerator",CE(xE(vV,1),xPc,62,0,[a.b]));a.u[6]=e;ilc(a,7,(ae(e.f==akc||e.f==bkc,lRc,e),cgc(e.k)));F=elc(a,"IThenable",CE(xE(vV,1),xPc,62,0,[a.o]));a.u[30]=F;G=(ae(F.f==akc|| +F.f==bkc,lRc,F),cgc(F.k));a.u[31]=G;pb=Wkc(a,(oo(),oo(),el("then",tb),new Sz("then",tb)));Sd(ETc);jDc(a.v,ETc);a.u[49]=pb;db=Nkc(a,tb,CE(xE(cV,1),xPc,23,0,[Mkc(a,tb,Tkc(a,CE(xE(cV,1),xPc,23,0,[Zkc(a,CE(xE(cV,1),xPc,23,0,[a.w,Ykc(a,G,CE(xE(cV,1),xPc,23,0,[a.w])),pb,L]))]))),Mkc(a,tb,Tkc(a,CE(xE(cV,1),xPc,23,0,[b])))]));cb=(ae(Hsc("",_sc(46))==-1,HMc,""),n8b(),new Wbc(rec,""));cb.d=db;bb=uic(yic(Iic(Bic(Aic(vic(xic(new Lic(a))),pPc),(Xd(cb.n==rec||cb.n==Sec),new L9b(Jec,cb))),CE(xE(vV,1),xPc,62,0,[a.w])), +a.o,a.w));Ejc(bb,new Wz(Sd(Ykc(a,G,CE(xE(cV,1),xPc,23,0,[a.w])))));a.u[37]=bb;ilc(a,36,(ae(bb.f==akc||bb.f==bkc,lRc,bb),cgc(bb.k)));h=uic(Dic(Bic(Aic(vic(xic(new Lic(a))),lPc),Tkc(a,CE(xE(cV,1),xPc,23,0,[b]))),j));zjc(h);a.u[10]=h;i=(ae(h.f==akc||h.f==bkc,lRc,h),cgc(h.k));a.u[9]=i;l=uic(Dic(Bic(Aic(vic(xic(new Lic(a))),"Date"),Tkc(a,CE(xE(cV,1),xPc,23,0,[tb,tb,tb,tb,tb,tb,tb]))),jb));zjc(l);a.u[13]=l;m=(ae(l.f==akc||l.f==bkc,lRc,l),cgc(l.k));a.u[12]=m;N=uic(Dic(Bic(Aic(vic(xic(new Lic(a))),oPc),Tkc(a, +CE(xE(cV,1),xPc,23,0,[b]))),U));zjc(N);a.u[35]=N;O=(ae(N.f==akc||N.f==bkc,lRc,N),cgc(N.k));a.u[34]=O;eb=uic(Fic(Bic(Aic(vic(xic(new Lic(a))),qPc),Tkc(a,CE(xE(cV,1),xPc,23,0,[b,b])))));zjc(eb);a.u[42]=eb;fb=(ae(eb.f==akc||eb.f==bkc,lRc,eb),cgc(eb.k));a.u[41]=fb;gb=uic(Dic(Bic(Aic(vic(xic(new Lic(a))),oMc),Tkc(a,CE(xE(cV,1),xPc,23,0,[b]))),jb));zjc(gb);a.u[44]=gb;hb=(ae(gb.f==akc||gb.f==bkc,lRc,gb),cgc(gb.k));a.u[43]=hb;lb=uic(Dic(Bic(Aic(vic(xic(new Lic(a))),rPc),Tkc(a,CE(xE(cV,1),xPc,23,0,[b]))), +nb));zjc(lb);a.u[47]=lb;mb=(ae(lb.f==akc||lb.f==bkc,lRc,lb),cgc(lb.k));a.u[46]=mb;M=Zkc(a,CE(xE(cV,1),xPc,23,0,[L,ub]));a.u[66]=M;$=Zkc(a,CE(xE(cV,1),xPc,23,0,[ab,nb]));a.u[67]=$;X=Zkc(a,CE(xE(cV,1),xPc,23,0,[ab,U,jb]));a.u[68]=X;Y=Zkc(a,CE(xE(cV,1),xPc,23,0,[ab,U,jb,j]));a.u[69]=Y;Z=Zkc(a,CE(xE(cV,1),xPc,23,0,[ab,U,jb,j,nb]));a.u[70]=Z;Q=Zkc(a,CE(xE(cV,1),xPc,23,0,[U,jb,j]));a.u[71]=Q;R=Zkc(a,CE(xE(cV,1),xPc,23,0,[U,jb,j,nb]));a.u[72]=R;T=Zkc(a,CE(xE(cV,1),xPc,23,0,[U,nb]));a.u[73]=T;ib=Zkc(a,CE(xE(cV, +1),xPc,23,0,[jb,nb]));a.u[74]=ib;P=Zkc(a,CE(xE(cV,1),xPc,23,0,[U,jb]));a.u[75]=P;S=Zkc(a,CE(xE(cV,1),xPc,23,0,[U,jb,nb]));a.u[76]=S;kb=Zkc(a,CE(xE(cV,1),xPc,23,0,[hb,jb]));a.u[54]=kb;V=Zkc(a,CE(xE(cV,1),xPc,23,0,[O,U]));a.u[55]=V;ob=Zkc(a,CE(xE(cV,1),xPc,23,0,[mb,nb]));a.u[56]=ob;sb=Okc(a,tb,CE(xE(cV,1),xPc,23,0,[tb]));a.u[63]=sb;rb=new nlc(a,new ghc(a,Vkc(a,true,CE(xE(cV,1),xPc,23,0,[tb])),tb),tb,akc);n.k=rb;Xd(!(!!rb.g||!rb.u));rb.p=o;a.u[62]=rb;a.u[17]=rb;H=Qkc(a,K,CE(xE(cV,1),xPc,23,0,[b]));a.u[64]= +H;r=uic(Dic(Bic(Aic(vic(xic(new Lic(a))),"global this"),Vkc(a,false,CE(xE(cV,1),xPc,23,0,[b]))),U));q=(ae(r.f==akc||r.f==bkc,lRc,r),cgc(r.k));a.u[61]=q;s=Qkc(a,b,CE(xE(cV,1),xPc,23,0,[K]));a.u[65]=s;jlc(a,aNc,W)}function iLb(a,b){var c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,A,B,C,D,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z;switch(b.p.f){case 42:return nLb(a,b);case 28:return jLb(a,b);case 75:return dMb(a,b);case 76:return eMb(a,b.a);case 77:return eKb(a.a,(Afc(),kfc),yKb(a.a,b.a));case 44:return iMb(a, +b);case 2:return pLb(a,b);case 14:return qLb(a,b);case 49:return CLb(a,b);case 17:return aMb(a,b);case 18:return bMb(a,b);case 22:return b.b.p==(n1b(),b0b)&&bKb(a.a,b,(IRb(),pRb)),fKb(a.a,(Afc(),adc),yKb(a.a,b.b),zKb(a.a,b.a));case 13:return tLb(a,b);case 8:return fKb(a.a,(Afc(),qdc),zKb(a.a,b.a),yKb(a.a,b.b));case 5:return dKb((d=a,b,d).a,(Afc(),tdc));case 6:return yLb(a,b);case 24:return dKb((e=a,b,e).a,(Afc(),idc));case 25:return dKb((f=a,b,f).a,(Afc(),lfc));case 11:return zLb(a,b);case 10:return h= +yKb(a.a,b.c),gKb(a.a,(Afc(),Hdc),h,yKb(a.a,b.b),zKb(a.a,b.a));case 1:return BLb(a,b);case 55:return fKb(a.a,(Afc(),Odc),yKb(a.a,b.b),yKb(a.a,b.a));case 46:return i=yKb(a.a,b.b),j=b.a,k=QLb(a,j),!k.Yh()&&!a.a.c&&eLb(a,k),fKb(a.a,(Afc(),Pdc),i,k);case 43:return gKb(a.a,(Afc(),Sdc),yKb(a.a,b.a),yKb(a.a,b.b),yKb(a.a,b.c));case 7:return FLb(a,b);case 20:return JLb(a,b);case 56:return XKb(a,b),yKb(a.a,b.a);case 26:return LLb(a,b.a,false);case 47:return PLb(a,b);case 29:return RLb(a,b);case 36:return bKb(a.a, +b,(IRb(),PQb)),fKb(a.a,(Afc(),fdc),yKb(a.a,b.a),yKb(a.a,b.b));case 37:return bKb(a.a,b,(IRb(),PQb)),l=yKb(a.a,b.c),m=yKb(a.a,b.a),n=v3b((ae(Hsc("",_sc(46))==-1,HMc,""),n8b(),new Wbc((Afc(),rec),"")),new J9b(Jec),m),I9b(n,m),o=fKb(a.a,fdc,l,n),c9b(o,M7b,1),c9b(o,k8b,b.b?1:0),o;case 89:return bKb(a.a,b,(IRb(),PQb)),gLb(a,b,hRb),p=eKb(a.a,(Afc(),fdc),yKb(a.a,b.d)),bLb(a,p,b.b),c9b(p,(n8b(),P7b),1),d9b(p,L7b,b.a),x9b(p,b.c),_Kb(a,b,p,b.a),p;case 38:return bKb(a.a,b,(IRb(),PQb)),q=fKb(a.a,(Afc(),fdc), +yKb(a.a,b.c),yKb(a.a,b.b)),b9b(q,(n8b(),N7b),true),b.b.i&&c9b(q,(Hbc(),zbc),1),_Kb(a,b,q,b.a),q;case 39:return bKb(a.a,b,(IRb(),PQb)),r=yKb(a.a,b.d),s=ALb(a,b.c),uKb(s,b.c),t=yKb(a.a,b.a),u=v3b((ae(Hsc("",_sc(46))==-1,HMc,""),n8b(),new Wbc((Afc(),rec),"")),s,t),I9b(u,t),v=fKb(a.a,fdc,r,u),c9b(v,O7b,1),c9b(v,k8b,b.b?1:0),v;case 15:return ZLb(a,b);case 45:return w=VKb(b.b.e),A=yKb(a.a,b.a),YKb(a,w,b.c==1,A);case 0:return mLb(a,b);case 27:return KLb(a,b);case 19:return cMb(a,b);case 21:return eKb(a.a, +(Afc(),mfc),yKb(a.a,b.a));case 23:return fMb(a,b);case 3:return iLb(a.a.t,b.a);case 12:return lMb(a,b);case 4:return kMb(a,b);case 9:return fKb(a.a,(Afc(),xfc),yKb(a.a,b.b),zKb(a.a,b.a));case 16:return fKb(a.a,(Afc(),yfc),yKb(a.a,b.b),zKb(a.a,b.a));case 41:return sLb(a,b);case 64:return dKb((g=a,b,g).a,(Afc(),tdc));case 57:return zKb(a.a,b.a);case 40:return iKb((c=a,b,c).a,(Afc(),rec),RRc);case 35:return WLb(a,b);case 33:return B=QLb(a,b.c),A9b(B,(Afc(),Qdc)),C=yKb(a.a,b.a),D=iKb(a.a,rec,""),uKb(D, +b.a),F=dKb(a.a,Jec),uKb(F,b.a),G=gKb(a.a,Jdc,D,F,C),uKb(G,b.a),r8b(B,G),bLb(a,G,b.d),x9b(B,b.b),B;case 34:return H=QLb(a,b.d),A9b(H,(Afc(),Yec)),I=ALb(a,b.c),uKb(I,b.c),J=yKb(a.a,b.a),K=iKb(a.a,rec,""),tKb(K,b.d),L=gKb(a.a,Jdc,K,I,J),uKb(L,b.a),r8b(H,L),x9b(H,b.b),H;case 62:return ALb(a,b);case 50:return rLb(a,b);case 58:return bKb(a.a,b,(IRb(),BRb)),dKb(a.a,(Afc(),ffc));case 94:return bKb(a.a,b,(IRb(),kRb)),dKb(a.a,(Afc(),yec));case 73:return M=dKb(a.a,(Afc(),zfc)),!!b.a&&q8b(M,yKb(a.a,b.a)),B9b(M, +b.b),M;case 95:return bKb(a.a,b,(IRb(),HQb)),N=dKb(a.a,(Afc(),Pcc)),q8b(N,yKb(a.a,b.a)),N;case 71:return bKb(a.a,b,(IRb(),ZQb)),O=yKb(a.a,b.c),gKb(a.a,(Afc(),Idc),O,yKb(a.a,b.b),zKb(a.a,b.a));case 72:return bKb(a.a,b,(IRb(),YQb)),P=yKb(a.a,b.c),gKb(a.a,(Afc(),Gdc),P,yKb(a.a,b.b),zKb(a.a,b.a));case 67:return wLb(a,b);case 68:return xLb(a,b);case 69:return GLb(a,b);case 70:return HLb(a,b);case 96:return bKb(a.a,b,(IRb(),TQb)),Q=yKb(a.a,b.a),eKb(a.a,(Afc(),rdc),Q);case 59:return kLb(a,b);case 61:return SLb(a, +b);case 60:return lLb(a,b);case 32:case 31:case 30:return a.a.d.Ah("unsupported language feature: array/generator comprehensions",a.a.q,(RJb(),b.o.b.b+1),b.o.b.a),iKb(a.a,(Afc(),rec),RRc);case 74:return uLb(a,b);case 65:return bKb(a.a,b,(IRb(),xRb)),R=AKb(a.a,b.a),R.n==(Afc(),Fec)?bKb(a.a,b.a,lRb):R.n==Acc&&bKb(a.a,b.a,EQb),eKb(a.a,Sec,R);case 63:return bKb(a.a,b,(IRb(),zRb)),eKb(a.a,(Afc(),_ec),yKb(a.a,b.a));case 78:return gMb(a,b);case 80:return S=yKb(a.a,b.a),bLb(a,S,b.b),S;case 81:return gLb(a, +b,(IRb(),qRb)),T=yKb(a.a,b.a),c9b(T,(n8b(),f8b),1),T;case 82:return VLb(a,b);case 83:return SJb(a.a,Hfc(yKb(a.a,b.a)));case 84:return XLb(a,b);case 85:return jMb(a,b);case 86:return DLb(a,b);case 79:return hMb(a,b);case 87:return ELb(a,b);case 88:return U=iKb(a.a,(Afc(),nec),b.e.a),bLb(a,U,b.b),x9b(U,b.d),c9b(U,(n8b(),f8b),b.c?1:0),_Kb(a,b,U,b.a),U;case 51:return ILb(a,b);case 54:return vLb(a,b);case 90:return gLb(a,b,(IRb(),FRb)),V=iKb(a.a,(Afc(),qfc),b.a.a),r8b(V,yKb(a.a,b.b)),V;case 91:return gLb(a, +b,(IRb(),DQb)),eKb(a.a,(Afc(),kdc),yKb(a.a,b.a));case 52:return NLb(a,b);case 92:return gLb(a,b,(IRb(),cRb)),W=yKb(a.a,b.b),X=H8b(W,(Hbc(),Nac)),X.n!=(Afc(),Dec)&&X.n!=dfc&&a.a.d.Ah("Index signature parameter type must be 'string' or 'number'",a.a.q,(RJb(),b.b.o.b.b+1),b.b.o.b.a),Y=eKb(a.a,_dc,W),bLb(a,Y,b.a),Y;case 93:return gLb(a,b,b.c?(IRb(),QQb):(IRb(),LQb)),Z=eKb(a.a,(Afc(),Zcc),yKb(a.a,b.a)),bLb(a,Z,b.d),aLb(a,Z,b.b),c9b(Z,(n8b(),Q7b),b.c?1:0),Z}return a.a.d.Ah("Unsupported syntax: "+b.p,a.a.q, +(RJb(),b.o.b.b+1),0),dKb(a.a,(Afc(),tdc))}function VMb(a,b,c){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,A,B,C,D,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,$,ab,bb,cb,db,eb,fb,gb,hb,ib,jb,kb,lb,mb,nb,ob,pb,qb,rb,sb,tb,ub,vb;u=a.j.g;i=lOb(a.j);e=a.j.n;d=Po(a.a,e);if(!d||e.length==0)BMb(a,_Qc,e,a.j.g,lOb(a.j));else{n6b(a.f,e,u,i);switch(d.f){case 0:Z4b(a.f.a)?zMb(a,"msg.jsdoc.nginject.extra",a.j.g,lOb(a.j)):Z6b(a.f);return IMb(a,!a.o?mOb(a.j):GMb(a));case 1:r6b(a.f)||CMb(a,WRc,a.j.g,lOb(a.j));return IMb(a, +!a.o?mOb(a.j):GMb(a));case 2:if(a.f.d){g=LMb(a);f=g.a;f.length==0?zMb(a,"msg.jsdoc.authormissing",a.j.g,lOb(a.j)):h6b(a.f,f);b=g.b}else b=IMb(a,!a.o?mOb(a.j):GMb(a));return b;case 4:v6b(a.f)||zMb(a,"msg.jsdoc.consistidgen",a.j.g,lOb(a.j));return IMb(a,!a.o?mOb(a.j):GMb(a));case 58:w7b(a.f)||CMb(a,WRc,a.j.g,lOb(a.j));return IMb(a,!a.o?mOb(a.j):GMb(a));case 50:m7b(a.f)||CMb(a,WRc,a.j.g,lOb(a.j));return IMb(a,!a.o?mOb(a.j):GMb(a));case 12:D6b(a.f)||CMb(a,WRc,a.j.g,lOb(a.j));return IMb(a,!a.o?mOb(a.j): +GMb(a));case 6:x6b(a.f)||(W4b(a.f.a)?CMb(a,XRc,a.j.g,lOb(a.j)):CMb(a,WRc,a.j.g,lOb(a.j)));return IMb(a,!a.o?mOb(a.j):GMb(a));case 8:Q6b(a.f)||CMb(a,"msg.jsdoc.record",a.j.g,lOb(a.j));return IMb(a,!a.o?mOb(a.j):GMb(a));case 10:A6b(a.f)||zMb(a,"msg.jsdoc.deprecated",a.j.g,lOb(a.j));J=KMb(a,b,a.g?0:2,false);I=J.a;I.length>0&&B6b(a.f,I);b=J.b;return b;case 26:R6b(a.f)||((a.f.a.a&2)!=0?CMb(a,XRc,a.j.g,lOb(a.j)):CMb(a,WRc,a.j.g,lOb(a.j)));return IMb(a,!a.o?mOb(a.j):GMb(a));case 11:if(y4b(a.f.a)!=null){zMb(a, +"msg.jsdoc.desc.extra",a.j.g,lOb(a.j));return IMb(a,!a.o?mOb(a.j):GMb(a))}else{k=KMb(a,b,a.g?0:2,false);j=k.a;C6b(a.f,j);b=k.b;return b}case 19:l="";if(a.f.d&&!NMb(a,64)){m=KMb(a,b,a.g?0:1,false);l=m.a;b=m.b}else b=IMb(a,!a.o?mOb(a.j):GMb(a));K6b(a.f,l)||zMb(a,"msg.jsdoc.fileoverview.extra",a.j.g,lOb(a.j));return b;case 28:case 43:H=KMb(a,b,0,true);G=H.a;G.length>0&&!!a.d&&i6b(a.d,G);b=H.b;return b;case 14:b=!a.o?mOb(a.j):GMb(a);u=a.j.g;i=lOb(a.j);jb=null;if(b!=(fOb(),TNb)&&b!=RNb){nb=UMb(a,b,a.j.g, +lOb(a.j),b==WNb,false);if(!!nb&&nb.n==(Afc(),bfc)){kb=nb.Wh();wMb.Sd(kb)||(nb=zNb(a,(Afc(),Qcc),nb))}jb=!nb?null:new H7b(nb,(P=H8b(a.n,(Hbc(),ybc)),!P?null:P.ug()))}else a.o=b;!jb&&(jb=FMb(a,RMb(a,YKc,a.j.g,lOb(a.j))));F6b(a.f,jb)||a.c.Bh(URc+occ(WRc,CE(xE(iW,1),mLc,1,5,[]))+VRc,(Q=H8b(a.n,(Hbc(),ybc)),!Q?null:Q.ug()),u,i);return IMb(a,!a.o?mOb(a.j):GMb(a));case 18:H6b(a.f)||zMb(a,"msg.jsdoc.expose",a.j.g,lOb(a.j));return IMb(a,!a.o?mOb(a.j):GMb(a));case 16:J6b(a.f)||zMb(a,"msg.jsdoc.externs",a.j.g, +lOb(a.j));return IMb(a,!a.o?mOb(a.j):GMb(a));case 57:t7b(a.f)||zMb(a,"msg.jsdoc.typesummary",a.j.g,lOb(a.j));return IMb(a,!a.o?mOb(a.j):GMb(a));case 15:case 23:xNb(a);b=!a.o?mOb(a.j):GMb(a);u=a.j.g;i=lOb(a.j);v=false;if(b==(fOb(),WNb)){b=!a.o?mOb(a.j):GMb(a);v=true}if(b==eOb){nb=UMb(a,b,u,i,v,true);u=a.j.g;i=lOb(a.j);nb=zNb(a,(Afc(),Qcc),nb);jb=!nb?null:new H7b(nb,(P=H8b(a.n,(Hbc(),ybc)),!P?null:P.ug()));if(d==(TIb(),$Hb))Ywc(c,new ENb(jb,a.j.g,lOb(a.j)));else{Xd(d==eIb);O6b(a.f,jb)||a.c.Bh(URc+occ("msg.jsdoc.implements.duplicate", +CE(xE(iW,1),mLc,1,5,[]))+VRc,(U=H8b(a.n,(Hbc(),ybc)),!U?null:U.ug()),u,i)}b=!a.o?mOb(a.j):GMb(a);v?b!=aOb?CMb(a,YRc,a.j.g,lOb(a.j)):b=!a.o?mOb(a.j):GMb(a):b!=TNb&&b!=SNb&&b!=RNb&&CMb(a,"msg.end.annotation.expected",a.j.g,lOb(a.j))}else b==NNb||b==$Nb?a.c.Bh(URc+occ("msg.jsdoc.implements.extraqualifier",CE(xE(iW,1),mLc,1,5,[]))+VRc,(V=H8b(a.n,(Hbc(),ybc)),!V?null:V.ug()),u,i):a.c.Bh(URc+occ("msg.no.type.name",CE(xE(iW,1),mLc,1,5,[]))+VRc,(W=H8b(a.n,(Hbc(),ybc)),!W?null:W.ug()),u,i);b=IMb(a,b);return b; +case 21:M6b(a.f)||zMb(a,"msg.jsdoc.hidden",a.j.g,lOb(a.j));return IMb(a,!a.o?mOb(a.j):GMb(a));case 27:xNb(a);v=false;if(OMb(a,(fOb(),WNb))){b=!a.o?mOb(a.j):GMb(a);v=true}if(OMb(a,eOb)){b=!a.o?mOb(a.j):GMb(a);S6b(a.f,FMb(a,V3b(a.j.n)))||CMb(a,"msg.jsdoc.lends.incompatible",a.j.g,lOb(a.j))}else CMb(a,"msg.jsdoc.lends.missing",a.j.g,lOb(a.j));v&&!OMb(a,aOb)&&CMb(a,YRc,a.j.g,lOb(a.j));return IMb(a,!a.o?mOb(a.j):GMb(a));case 29:A=KMb(a,b,a.g?0:2,false);w=A.a;b=A.b;V6b(a.f,w)||zMb(a,"msg.jsdoc.meaning.extra", +a.j.g,lOb(a.j));return b;case 3:xNb(a);return XMb(a,!a.o?mOb(a.j):GMb(a));case 34:_6b(a.f)||zMb(a,"msg.jsdoc.nocompile",a.j.g,lOb(a.j));return IMb(a,!a.o?mOb(a.j):GMb(a));case 33:$6b(a.f)||zMb(a,"msg.jsdoc.nocollapse",a.j.g,lOb(a.j));return IMb(a,!a.o?mOb(a.j):GMb(a));case 35:a7b(a.f)||zMb(a,"msg.jsdoc.noinline",a.j.g,lOb(a.j));return IMb(a,!a.o?mOb(a.j):GMb(a));case 37:return IMb(a,!a.o?mOb(a.j):GMb(a));case 25:case 38:e7b(a.f)||CMb(a,"msg.jsdoc.override",a.j.g,lOb(a.j));return IMb(a,!a.o?mOb(a.j): +GMb(a));case 41:$4b(a.f.a)?zMb(a,"msg.jsdoc.polymer.extra",a.j.g,lOb(a.j)):h7b(a.f);return IMb(a,!a.o?mOb(a.j):GMb(a));case 42:_4b(a.f.a)?zMb(a,"msg.jsdoc.polymerBehavior.extra",a.j.g,lOb(a.j)):i7b(a.f);return IMb(a,!a.o?mOb(a.j):GMb(a));case 7:V4b(a.f.a)?zMb(a,"msg.jsdoc.customElement.extra",a.j.g,lOb(a.j)):y6b(a.f);return IMb(a,!a.o?mOb(a.j):GMb(a));case 30:X4b(a.f.a)?zMb(a,"msg.jsdoc.mixinClass.extra",a.j.g,lOb(a.j)):W6b(a.f);return IMb(a,!a.o?mOb(a.j):GMb(a));case 31:Y4b(a.f.a)?zMb(a,"msg.jsdoc.mixinFunction.extra", +a.j.g,lOb(a.j)):X6b(a.f);return IMb(a,!a.o?mOb(a.j):GMb(a));case 54:{xNb(a);b=!a.o?mOb(a.j):GMb(a);u=a.j.g;i=lOb(a.j);jb=null;if(b==(fOb(),WNb)){jb=FMb(a,UMb(a,b,a.j.g,lOb(a.j),b==WNb,false));if(!jb)return IMb(a,!a.o?mOb(a.j):GMb(a))}b=GMb(a);r7b(a.f,jb);r=NMb(a,64);if(a.f.d&&!r){k=KMb(a,b,a.g?0:2,false);j=k.a;j.length>0&&q7b(a.f,jb,j);b=k.b}else b=IMb(a,!a.o?mOb(a.j):GMb(a));return b}case 40:xNb(a);b=!a.o?mOb(a.j):GMb(a);u=a.j.g;i=lOb(a.j);jb=null;o=false;if(b==(fOb(),WNb)){jb=FMb(a,(Kd(b==WNb), +sb=a.j.g,tb=lOb(a.j),ub=(Kd(b==WNb),xNb(a),vb=gNb(a,!a.o?mOb(a.j):GMb(a)),!!vb&&(OMb(a,aOb)?!a.o?mOb(a.j):GMb(a):(CMb(a,YRc,a.j.g,lOb(a.j)),null)),vb),uNb(a,sb,tb,ub,true),ub));if(!jb)return IMb(a,!a.o?mOb(a.j):GMb(a));xNb(a);b=!a.o?mOb(a.j):GMb(a);u=a.j.g;i=lOb(a.j);o=true}B=null;s=YNb==b;s&&(b=!a.o?mOb(a.j):GMb(a));if(eOb!=b)a.c.Bh(URc+occ("msg.missing.variable.name",CE(xE(iW,1),mLc,1,5,[]))+VRc,(O=H8b(a.n,(Hbc(),ybc)),!O?null:O.ug()),u,i);else{o||yMb(a,a.j.g,lOb(a.j));B=a.j.n;if(s){b=!a.o?mOb(a.j): +GMb(a);if(UNb==b){b=!a.o?mOb(a.j):GMb(a);eOb==b&&(b=!a.o?mOb(a.j):GMb(a))}cOb!=b?(CMb(a,"msg.jsdoc.missing.rb",a.j.g,lOb(a.j)),null):!!jb&&(jb=jb.a.n==(Afc(),ydc)||jb.a.n==sdc?jb:new H7b(new L9b(ydc,jb.a),jb.b))}G7b();if(F7b.test(B))f7b(a.f,B,jb)||(l6b(a.f,B)?a.c.Bh(URc+occ("msg.dup.variable.name",CE(xE(iW,1),mLc,1,5,[B]))+VRc,(P=H8b(a.n,(Hbc(),ybc)),!P?null:P.ug()),u,i):a.c.Bh(URc+occ(WRc,CE(xE(iW,1),mLc,1,5,[B]))+VRc,(O=H8b(a.n,(Hbc(),ybc)),!O?null:O.ug()),u,i));else{a.c.Bh(occ("msg.invalid.variable.name", +CE(xE(iW,1),mLc,1,5,[B])),(O=H8b(a.n,(Hbc(),ybc)),!O?null:O.ug()),u,i);B=null}}if(B==null){b=IMb(a,b);return b}o6b(a.f,B,a.n,u,i);if(a.f.d&&b!=MNb){F=KMb(a,b,a.g?0:2,false);D=F.a;D.length>0&&g7b(a.f,B,D);b=F.b}else b!=RNb&&b!=SNb&&(b=IMb(a,!a.o?mOb(a.j):GMb(a)));return b;case 36:b7b(a.f)||zMb(a,"msg.jsdoc.nosideeffects",a.j.g,lOb(a.j));return IMb(a,!a.o?mOb(a.j):GMb(a));case 32:b=eNb(a,!a.o?mOb(a.j):GMb(a));return b;case 24:P6b(a.f)||CMb(a,"msg.jsdoc.implicitcast",a.j.g,lOb(a.j));return IMb(a,!a.o? +mOb(a.j):GMb(a));case 48:if(a.f.d){L=LMb(a);K=L.a;K.length==0?zMb(a,"msg.jsdoc.seemissing",a.j.g,lOb(a.j)):j6b(a.f,K);b=L.b}else b=IMb(a,!a.o?mOb(a.j):GMb(a));return b;case 49:l7b(a.f)||zMb(a,"msg.jsdoc.stableidgen",a.j.g,lOb(a.j));return IMb(a,!a.o?mOb(a.j):GMb(a));case 51:b=kNb(a,!a.o?mOb(a.j):GMb(a));return b;case 52:{db=a.j.g;bb=lOb(a.j);cb=KMb(a,b,1,false);fb=cb.a;ob="";t=false;pb=true;if(fb.indexOf(":=")!=-1){ib=fb.indexOf(":=");eb=fb.substr(0,ib);if(fb.indexOf("=:")!=-1){t=true;gb=fb.indexOf("=:"); +ob=Ysc(fb.substr(ib+2,gb-(ib+2)))}else{pb=false;a.c.Bh(URc+occ("msg.jsdoc.typetransformation.missing.delimiter",CE(xE(iW,1),mLc,1,5,[]))+VRc,(O=H8b(a.n,(Hbc(),ybc)),!O?null:O.ug()),db,bb)}}else fb.indexOf(gMc)!=-1?eb=Vsc(fb,0,Hsc(fb,_sc(10))):eb=fb;C=Te(Ve(Ye(new nc(44)),(Hc(),Fc)),eb);if(C.b.Yd()==1&&C.a.ce(0).length==0)a.c.Bh(URc+occ("msg.jsdoc.templatemissing",CE(xE(iW,1),mLc,1,5,[]))+VRc,(O=H8b(a.n,(Hbc(),ybc)),!O?null:O.ug()),db,bb);else for(lb=new Wzc(C.b.Vd());lb.b.Id();){kb=lb.b.Jd();kb.length!= +0&&ac(new Cc((rc(),qc),new nc(95)),kb)?t||o7b(a.f,kb)||a.c.Bh(URc+occ(ZRc,CE(xE(iW,1),mLc,1,5,[]))+VRc,(P=H8b(a.n,(Hbc(),ybc)),!P?null:P.ug()),db,bb):a.c.Bh(URc+occ("msg.jsdoc.template.invalid.type.name",CE(xE(iW,1),mLc,1,5,[]))+VRc,(O=H8b(a.n,(Hbc(),ybc)),!O?null:O.ug()),db,bb)}if(t){C.b.Yd()>1&&a.c.Bh(URc+occ("msg.jsdoc.typetransformation.with.multiple.names",CE(xE(iW,1),mLc,1,5,[]))+VRc,(P=H8b(a.n,(Hbc(),ybc)),!P?null:P.ug()),db,bb);if(ob.length==0){pb=false;a.c.Bh(URc+occ("msg.jsdoc.typetransformation.expression.missing", +CE(xE(iW,1),mLc,1,5,[]))+VRc,(O=H8b(a.n,(Hbc(),ybc)),!O?null:O.ug()),db,bb)}if(pb){hb=new oPb(ob,H8b(a.n,(Hbc(),ybc)),a.c,db,bb);OOb(hb)&&!u7b(a.f,C.a.ce(0),hb.e)&&a.c.Bh(URc+occ(ZRc,CE(xE(iW,1),mLc,1,5,[]))+VRc,(O=H8b(a.n,ybc),!O?null:O.ug()),db,bb)}}b=cb.b;return b}case 22:b=cNb(a,!a.o?mOb(a.j):GMb(a));return b;case 60:z7b(a.f)||zMb(a,"msg.jsdoc.wizaction",a.j.g,lOb(a.j));return IMb(a,!a.o?mOb(a.j):GMb(a));case 13:{cb=LMb(a);C=Te(Ve(Ye(new nc(44)),(Hc(),Fc)),cb.a);C.a.Ud()||C.a.ce(0).length==0? +CMb(a,"msg.jsdoc.disposeparameter.missing",a.j.g,lOb(a.j)):E6b(a.f,C)||CMb(a,"msg.jsdoc.disposeparameter.error",a.j.g,lOb(a.j));b=cb.b;return b}case 59:rb=LMb(a);qb=rb.a;qb.length==0?zMb(a,"msg.jsdoc.versionmissing",a.j.g,lOb(a.j)):x7b(a.f,qb)||zMb(a,"msg.jsdoc.extraversion",a.j.g,lOb(a.j));b=rb.b;return b;case 5:case 20:case 9:case 17:case 47:case 39:case 44:case 45:case 46:case 53:case 55:case 56:u=a.j.g;i=lOb(a.j);mb=null;p=NMb(a,123);q=d==(TIb(),vIb)||d==AIb||d==BIb||d==CIb||d==PHb||d==bIb||d== +YHb;h=q||d==EIb;jb=null;d==EIb&&!p&&yMb(a,a.j.g,lOb(a.j));if(p||!h){xNb(a);b=!a.o?mOb(a.j):GMb(a);mb=UMb(a,b,a.j.g,lOb(a.j),b==(fOb(),WNb),false);d==KIb&&(mb=zNb(a,(Afc(),Qcc),mb));jb=!mb?null:new H7b(mb,(O=H8b(a.n,(Hbc(),ybc)),!O?null:O.ug()))}n=!jb&&!h;if(!n){(!!jb&&q||d==MIb)&&!s7b(a.f,jb)&&a.c.Bh(URc+occ(WRc,CE(xE(iW,1),mLc,1,5,[]))+VRc,(P=H8b(a.n,(Hbc(),ybc)),!P?null:P.ug()),u,i);r=NMb(a,64);switch(d.f){case 5:w6b(a.f)||zMb(a,"msg.jsdoc.const",a.j.g,lOb(a.j));break;case 20:L6b(a.f)||CMb(a,"msg.jsdoc.final", +a.j.g,lOb(a.j));break;case 9:z6b(a.f,jb)||a.c.Bh(occ("msg.jsdoc.define",CE(xE(iW,1),mLc,1,5,[])),(U=H8b(a.n,(Hbc(),ybc)),!U?null:U.ug()),u,i);if(!r)return tNb(a,b);break;case 17:G6b(a.f)?y7b(a.f,(e6b(),d6b))||a.c.Bh(occ($Rc,CE(xE(iW,1),mLc,1,5,[])),(X=H8b(a.n,(Hbc(),ybc)),!X?null:X.ug()),u,i):a.c.Bh(occ("msg.jsdoc.export",CE(xE(iW,1),mLc,1,5,[])),(Y=H8b(a.n,(Hbc(),ybc)),!Y?null:Y.ug()),u,i);if(!r)return tNb(a,b);break;case 44:y7b(a.f,(e6b(),b6b))||a.c.Bh(occ($Rc,CE(xE(iW,1),mLc,1,5,[])),(Z=H8b(a.n, +(Hbc(),ybc)),!Z?null:Z.ug()),u,i);if(!r)return tNb(a,b);break;case 39:y7b(a.f,(e6b(),a6b))||a.c.Bh(occ($Rc,CE(xE(iW,1),mLc,1,5,[])),($=H8b(a.n,(Hbc(),ybc)),!$?null:$.ug()),u,i);if(!r)return tNb(a,b);break;case 45:y7b(a.f,(e6b(),c6b))||a.c.Bh(occ($Rc,CE(xE(iW,1),mLc,1,5,[])),(ab=H8b(a.n,(Hbc(),ybc)),!ab?null:ab.ug()),u,i);if(!r)return tNb(a,b);break;case 46:y7b(a.f,(e6b(),d6b))||a.c.Bh(occ($Rc,CE(xE(iW,1),mLc,1,5,[])),(R=H8b(a.n,(Hbc(),ybc)),!R?null:R.ug()),u,i);if(!r)return tNb(a,b);break;case 47:!jb&& +(jb=FMb(a,QMb(a,(Afc(),Pec))));if(!k7b(a.f,jb)){a.c.Bh(URc+occ(WRc,CE(xE(iW,1),mLc,1,5,[]))+VRc,(S=H8b(a.n,(Hbc(),ybc)),!S?null:S.ug()),u,i);break}if(a.f.d&&!r){N=KMb(a,b,a.g?0:2,false);M=N.a;M.length>0&&j7b(a.f,M);b=N.b}else b=IMb(a,!a.o?mOb(a.j):GMb(a));return b;case 53:p7b(a.f,jb)||a.c.Bh(URc+occ(WRc,CE(xE(iW,1),mLc,1,5,[]))+VRc,(T=H8b(a.n,(Hbc(),ybc)),!T?null:T.ug()),u,i);break;case 56:v7b(a.f,jb)||a.c.Bh(URc+occ(WRc,CE(xE(iW,1),mLc,1,5,[]))+VRc,(O=H8b(a.n,(Hbc(),ybc)),!O?null:O.ug()),u,i)}}return IMb(a, +!a.o?mOb(a.j):GMb(a))}}return!a.o?mOb(a.j):GMb(a)}function q4(a,b,c){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,A,B,C,D,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,$,ab,bb,cb,db,eb,fb,gb;if(a.i&&!!H8b(b,(Hbc(),mbc))){G=Bmb(a.e,H8b(b,(Hbc(),mbc)));Esc(G,"/** */ ")||L3(a.c,G)}cb=b.n;N=$qb(cb);p=C8b(b);t=b.c;H=b.c?b.c.i:null;if(N!=null&&t!=H){be(p==2,N,p);O=H4(a,b);Y=c.b?c:(_4(),W4);K=(c==(_4(),Z4)||c.a)&&t.n==(Afc(),Fec);b.n==(Afc(),Ccc)&&K&&L3(a.c,"(");if(aqb(b)||cb==zdc){v4(a,t,O+1,c);N3(a.c,N,true); +v4(a,H,O,Y)}else K4(a,b,cb,N,c,Y,O,O+1);b.n==Ccc&&K&&L3(a.c,")");return}a.c.eh(b);switch(cb.f){case 44:{Xd(t.f.n==(Afc(),Vcc)&&!N8b(t.f));Xd(p>=2&&p<=3);L3(a.c,"try");a.fh(t,(_4(),W4));n=t.f.c;!!n&&a.fh(n,W4);if(p==3){a.c.$g();L3(a.c,DNc);a.fh(H,W4)}break}case 78:Yd(p==2,b);a.c.$g();L3(a.c,"catch");a.c.$g();if(t.n!=(Afc(),tdc)){L3(a.c,"(");a.fh(t,(_4(),W4));L3(a.c,")")}a.fh(H,(_4(),W4));break;case 39:Yd(p==1,b);L3(a.c,"throw");a.c.$g();a.fh(t,(_4(),W4));a.c.Wg(true);break;case 0:L3(a.c,ENc);if(p== +1){a.c.$g();if(a.i&&!!H8b(t,(Hbc(),mbc))){L3(a.c,"(");a.fh(t,(_4(),W4));L3(a.c,")")}else a.fh(t,(_4(),W4))}else Yd(p==0,b);a.c.Wg(false);break;case 76:L3(a.c,"var ");z4(a,t,false,c.b?c:(_4(),W4),",");(!b.g||(ppb(),b.n!=(Afc(),pec)&&Jqb(b.g)))&&a.c.Wg(false);break;case 88:L3(a.c,"const ");z4(a,t,false,c.b?c:(_4(),W4),",");(!b.g||(ppb(),b.n!=(Afc(),pec)&&Jqb(b.g)))&&a.c.Wg(false);break;case 100:L3(a.c,"let ");z4(a,t,false,c.b?c:(_4(),W4),",");(!b.g||(ppb(),b.n!=(Afc(),pec)&&Jqb(b.g)))&&a.c.Wg(false); +break;case 90:Yd(b.Wh().length!=0,b);x4(a,b.Wh());break;case 95:a.fh(t,(_4(),W4));if(t!=H){Yd(p==2,b);N3(a.c,"=",true);a.fh(H,W4)}break;case 29:a.n&&H8b(b,(Hbc(),sbc))!=null?x4(a,H8b(b,(Hbc(),sbc))):x4(a,b.Wh());E8b(b,(n8b(),f8b))!=0&&L3(a.c,"?");G4(a,b);if(!!t&&t.n!=(Afc(),tdc)){Yd(p==1,b);N3(a.c,"=",true);t.n==(Afc(),edc)||t.n==_cc&&t.c.n==edc?v4(a,t,arb(Ccc),(_4(),W4)):v4(a,t,0,c.b?c:(_4(),W4))}break;case 42:L3(a.c,"[");t4(a,t);L3(a.c,"]");break;case 93:L3(a.c,"[");t4(a,t);L3(a.c,"]");G4(a,b); +break;case 45:if(Q8b(b.g)&&!!b.c&&!b.c.f&&t.n==(Afc(),rec)&&!lQb(a.f,(IRb(),GRb)))a.fh(t,(_4(),W4));else{L3(a.c,"(");z4(a,t,true,(_4(),W4),",");L3(a.c,")")}break;case 121:a.fh(t,(_4(),W4));G4(a,b);N3(a.c,"=",true);v4(a,t.f,1,W4);break;case 46:Yd(p==2,b);K4(a,b,(Afc(),edc),",",c,c.b?c:(_4(),W4),0,0);break;case 30:Yd(p==0,b);a.c.Kg(b.Uh(),b);break;case 25:case 79:case 19:case 20:case 21:{Yd(p==1,b);N3(a.c,_qb(cb),false);v4(a,t,arb(cb),(_4(),W4));break}case 22:{Yd(p==1,b);if(b.c.n==(Afc(),Cec))a.c.Kg(-b.c.Uh(), +b.c);else{N3(a.c,_qb(cb),false);v4(a,t,arb(cb),(_4(),W4))}break}case 60:{Yd(p==3,b);O=arb(cb);Y=c.b?c:(_4(),W4);v4(a,t,O+1,c);N3(a.c,"?",true);v4(a,t.f,1,Y);N3(a.c,":",true);v4(a,H,1,Y);break}case 38:if(t.n!=(Afc(),bfc)||H.n!=bfc)throw gZ(new Hqc("Expected children to be strings"));W="/"+J4(a,t.Wh(),'"',"'","\\",false,true)+"/";if(p==2)r4(a,W+H.Wh());else{Yd(p==1,b);L3(a.c,W)}break;case 65:{if(b.Fj!=HU)throw gZ(new Hqc(FNc));Yd(p==3,b);b.n==(Afc(),Jdc)&&E8b(b,(Hbc(),Fac))!=0?(Yd(t.Wh().length==0, +t),db=(eb=b.g,!!eb&&(ppb(),bqb(eb.n)||Mqb(eb.n)||Oqb(eb.n)||eb.n==hfc||eb.n==Pdc||(eb.n==Odc||eb.n==Ycc||eb.n==Sdc)&&(fb=b.g,!!fb&&b==fb.c))),db&&L3(a.c,"("),gb=H8b(t,(n8b(),Y7b)),!!gb&&a.fh(gb,(_4(),W4)),b.n==Jdc&&E8b(b,(Hbc(),Gac))!=0&&L3(a.c,GNc),a.fh(t.f,(_4(),W4)),G4(a,b),N3(a.c,"=>",true),H.n==Vcc?a.fh(H,W4):v4(a,H,arb(edc)+1,c.b?Y4:X4),a.c.Tg(c==$4),db&&L3(a.c,")"),undefined):w4(a,b,t,H,c);break}case 114:L3(a.c,HNc);a.fh(t,(_4(),W4));G4(a,b);break;case 115:L3(a.c,HNc);a.fh(b.c,(_4(),W4));break; +case 109:L3(a.c,INc);E8b(b,(n8b(),U7b))!=0&&L3(a.c,JNc);if(E8b(b,T7b)!=0){L3(a.c,"*");Yd(!!t&&t.n==(Afc(),tdc),b)}else a.fh(t,(_4(),W4));if(p==2){L3(a.c,KNc);a.fh(H,(_4(),W4))}I4(a,t,c);break;case 105:L3(a.c,LNc);Z=t.f;if(t.n!=(Afc(),tdc)){a.fh(t,(_4(),W4));Z.n==tdc||a.c.Yg()}Z.n==tdc||a.fh(Z,(_4(),W4));(t.n!=tdc||Z.n!=tdc)&&L3(a.c,KNc);a.fh(H,(_4(),W4));a.c.Wg(false);break;case 110:case 106:L3(a.c,"{");for(j=t;j;j=j.f){j!=t&&a.c.Yg();a.fh(j,(_4(),W4))}L3(a.c,"}");break;case 111:case 107:a.fh(t,(_4(), +W4));if(E8b(b,(Hbc(),kbc))!=0&&Esc(t.Wh(),H.Wh()))break;L3(a.c,"as");a.fh(H,W4);break;case 108:L3(a.c,"*");L3(a.c,"as");r4(a,b.Wh());break;case 113:L3(a.c,"import(");v4(a,t,arb(cb),c);L3(a.c,")");break;case 96:{Yd(p==3,b);q=c==(_4(),Z4);q&&L3(a.c,"(");J=t;$=t.f;I=H;L3(a.c,MNc);J.n==(Afc(),tdc)||a.fh(J,W4);A=H8b(t,(n8b(),Y7b));!!A&&a.fh(A,W4);if($.n!=tdc){L3(a.c,NNc);a.fh($,W4)}D=H8b(b,$7b);if(D){L3(a.c,ONc);o=D.c;a.fh(o,W4);while(o=o.f){L3(a.c,",");a.c.$g();a.fh(o,W4)}}a.fh(I,W4);S3(a.c,c==$4);q&& +L3(a.c,")")}break;case 97:case 153:case 163:O3(a.c);for(k=t;k;k=k.f){a.fh(k,(_4(),W4));I4(a,k,c);a.c.Ug()}R3(a.c,false);break;case 155:O3(a.c);for(l=t;l;l=l.f){a.fh(l,(_4(),W4));!!l.f&&L3(a.c,",");a.c.Ug()}R3(a.c,false);break;case 86:case 87:case 98:case 159:{Xd(b.g.n==(Afc(),Eec)||b.g.n==cdc||b.g.n==dec||b.g.n==Qec||b.g.n==_dc);e=H8b(b,(n8b(),L7b));!!e&&r4(a,(e.e!=null?e.e:""+e.f).toLowerCase()+" ");E8b(b,(Hbc(),zbc))!=0&&L3(a.c,"static ");b.n==mec&&R8b(b.c)&&L3(a.c,"async ");if(b.n!=nec&&E8b(b.c, +Xac)!=0){Yd(cb==mec,b);L3(a.c,"*")}switch(cb.f){case 86:Yd(!t.c.f.c,b);L3(a.c,"get ");break;case 87:Yd(O8b(t.c.f),b);L3(a.c,"set ")}J=b.Wh();if(b.n==nec){r4(a,b.Wh());E8b(b,f8b)!=0&&L3(a.c,"?");G4(a,b)}else{Yd(p==1,b);Yd(t.n==Jdc,t);Yd(t.c.Wh().length==0,t);u=t;P=u.c.f;h=u.c?u.c.i:null;if(!b.Yh()&&(G7b(),F7b.test(J))&&wqb(J)){L3(a.c,J);F4(a,u.c)}else{r=Q4(J);isNaN(r)?y4(a,b):a.c.Kg(r,b)}E8b(u,f8b)!=0&&L3(a.c,"?");a.fh(P,(_4(),W4));G4(a,u);a.fh(h,W4)}break}case 85:case 112:case 82:case 81:{if(b.Fj!= +HU)throw gZ(new Hqc(FNc));V=b.n==(Afc(),Vcc)&&E8b(b,(Hbc(),Abc))==0;V&&O3(a.c);U=cb==Xec||cb==Vcc&&!V&&b.g.n==Xec;for(m=t;m;m=m.f){a.fh(m,(_4(),$4));(m.n==Jdc||m.n==bdc)&&a.c._g();U&&a.c.ah()}V&&R3(a.c,a.c.Qg(b,c==(_4(),$4)));break}case 72:Yd(p==4,b);L3(a.c,"for");a.c.$g();L3(a.c,"(");ppb();!!t&&(t.n==(Afc(),ufc)||t.n==jec||t.n==gdc)?a.fh(t,(_4(),V4)):v4(a,t,0,(_4(),V4));L3(a.c,";");t.f.n==(Afc(),tdc)||a.c.$g();a.fh(t.f,(_4(),W4));L3(a.c,";");t.f.f.n==tdc||a.c.$g();a.fh(t.f.f,W4);L3(a.c,")");A4(a, +H,c==U4?U4:W4,false);break;case 73:Yd(p==3,b);L3(a.c,"for");a.c.$g();L3(a.c,"(");a.fh(t,(_4(),W4));L3(a.c,"in");a.fh(t.f,W4);L3(a.c,")");A4(a,H,c==U4?U4:W4,false);break;case 101:Yd(p==3,b);L3(a.c,"for");a.c.$g();L3(a.c,"(");a.fh(t,(_4(),W4));a.c.$g();L3(a.c,"of");a.c.$g();a.fh(t.f,W4);L3(a.c,")");A4(a,H,c==U4?U4:W4,false);break;case 102:Yd(p==3,b);L3(a.c,"for await");a.c.$g();L3(a.c,"(");a.fh(t,(_4(),W4));a.c.$g();L3(a.c,"of");a.c.$g();a.fh(t.f,W4);L3(a.c,")");A4(a,H,c==U4?U4:W4,false);break;case 71:Yd(p== +2,b);L3(a.c,"do");A4(a,t,(_4(),W4),false);a.c.$g();L3(a.c,PNc);a.c.$g();L3(a.c,"(");a.fh(H,W4);L3(a.c,")");a.c.Wg(false);break;case 70:Yd(p==2,b);L3(a.c,PNc);a.c.$g();L3(a.c,"(");a.fh(t,(_4(),W4));L3(a.c,")");A4(a,H,c==U4?U4:W4,false);break;case 80:Yd(p==0,b);break;case 26:{if(a.n&&H8b(b,(Hbc(),sbc))!=null){_8b(b.c,"$jscomp.scope")&&b.g.n==(Afc(),Ccc)&&L3(a.c,"var ");x4(a,H8b(b,(Hbc(),sbc)));break}Zd(p==2,"Bad GETPROP: expected 2 children, but got %s",p);Yd(H.n==(Afc(),bfc),"Bad GETPROP: RHS should be STRING"); +K=t.n==Cec;K&&L3(a.c,"(");v4(a,t,arb(cb),c);K&&L3(a.c,")");if(a.j&&Efc(H.Wh())){L3(a.c,"[");a.fh(H,(_4(),W4));L3(a.c,"]")}else{L3(a.c,".");x4(a,H.Wh())}break}case 27:_d(p==2,p,b);v4(a,t,arb(cb),c);L3(a.c,"[");a.fh(t.f,(_4(),W4));L3(a.c,"]");break;case 77:Yd(p==2,b);L3(a.c,"with(");a.fh(t,(_4(),W4));L3(a.c,")");A4(a,H,c==U4?U4:W4,false);break;case 63:case 64:{Yd(p==1,b);M=cb==(Afc(),$dc)?"++":"--";R=E8b(b,(n8b(),_7b))!=0;if(R){v4(a,t,arb(cb),c);N3(a.c,M,false)}else{N3(a.c,M,false);a.fh(t,(_4(),W4))}break}case 28:if(t.n== +(Afc(),rec)&&Esc("eval",t.Wh())&&E8b(t,(n8b(),R7b))==0||E8b(b,(n8b(),W7b))!=0&&(ppb(),t.n==Pdc||t.n==Odc)){L3(a.c,"(0,");v4(a,t,arb(edc),(_4(),W4));L3(a.c,")")}else v4(a,t,arb(cb),c);g=t.f;L3(a.c,"(");z4(a,g,true,(_4(),W4),",");L3(a.c,")");break;case 66:Yd(p==2||p==3,b);B=p==3;f=c==(_4(),U4)&&!B;f&&O3(a.c);L3(a.c,"if");a.c.$g();L3(a.c,"(");a.fh(t,W4);L3(a.c,")");if(B){A4(a,t.f,U4,false);a.c.$g();L3(a.c,"else");A4(a,H,c==U4?U4:W4,false)}else A4(a,t.f,W4,false);f&&R3(a.c,false);break;case 32:Yd(p== +0,b);L3(a.c,jLc);break;case 33:Yd(p==0,b);L3(a.c,FMc);break;case 99:Yd(p==0,b);L3(a.c,DMc);break;case 122:Yd(p==0,b);L3(a.c,QNc);break;case 103:L3(a.c,RNc);if(E8b(b,(Hbc(),Gbc))!=0){Sd(t);L3(a.c,"*")}if(t){a.c.$g();v4(a,t,arb(cb),(_4(),W4))}break;case 104:L3(a.c,"await ");v4(a,t,arb(cb),(_4(),W4));break;case 34:Yd(p==0,b);L3(a.c,SNc);break;case 35:Yd(p==0,b);L3(a.c,TNc);break;case 75:Yd(p<=1,b);L3(a.c,UNc);if(p==1){if(t.n!=(Afc(),fec))throw gZ(new Hqc(VNc));L3(a.c," ");a.fh(t,(_4(),W4))}a.c.Wg(false); +break;case 89:Yd(p==0,b);L3(a.c,WNc);a.c.Wg(false);break;case 74:Yd(p<=1,b);L3(a.c,"break");if(p==1){if(t.n!=(Afc(),fec))throw gZ(new Hqc(VNc));L3(a.c," ");a.fh(t,(_4(),W4))}a.c.Wg(false);break;case 84:Yd(p==1,b);a.fh(t,(_4(),Z4));a.c.Wg(false);break;case 23:L3(a.c,"new ");S=arb(cb);T=arb(t.n);T==S&&(S=S+1);wpb(t,(Afc(),Ycc),(ppb(),kpb))&&(S=arb(t.n)+1);v4(a,t,S,(_4(),W4));L=t.f;if(L){L3(a.c,"(");z4(a,L,true,W4,",");L3(a.c,")")}break;case 91:C4(a,b);break;case 31:ae(p==0,"String node %s may not have children", +b);y4(a,b);break;case 24:Yd(p==1,b);L3(a.c,"delete ");a.fh(t,(_4(),W4));break;case 43:{K=c==(_4(),Z4)||c.a;K&&L3(a.c,"(");L3(a.c,"{");for(m=t;m;m=m.f){m!=t&&a.c.Yg();Yd((ppb(),m.n==(Afc(),cfc)||m.n==Qdc||m.n==Yec||m.n==mec||m.n==fdc||m.n==_ec),m);a.fh(m,W4)}L3(a.c,"}");K&&L3(a.c,")");break}case 116:d=H8b(b,(n8b(),L7b));!!d&&r4(a,(d.e!=null?d.e:""+d.f).toLowerCase()+" ");E8b(b,k8b)!=0&&L3(a.c,"static ");if(E8b(b,M7b)!=0)L3(a.c,"get ");else if(E8b(b,O7b)!=0)L3(a.c,"set ");else{H.n==(Afc(),Jdc)&&E8b(H, +(Hbc(),Gac))!=0&&L3(a.c,GNc);E8b(H,X7b)!=0&&L3(a.c,"*")}L3(a.c,"[");a.fh(t,(_4(),W4));L3(a.c,"]");G4(a,b);if(E8b(b,N7b)!=0||E8b(b,M7b)!=0||E8b(b,O7b)!=0){v=t.f;Q=v.c.f;h=v.c?v.c.i:null;a.fh(Q,W4);a.fh(h,W4)}else{F=b.g.n==(Afc(),cdc);C=t.f;if(C){Yd(!F,"initializers should only exist in object literals, not classes");N3(a.c,":",false);a.fh(C,W4)}else Yd(E8b(b,P7b)!=0,b)}break;case 94:B4(a,b);G4(a,b);break;case 67:L3(a.c,"switch(");a.fh(t,(_4(),W4));L3(a.c,")");O3(a.c);s4(a,t.f);R3(a.c,c==$4);break; +case 68:Yd(p==2,b);L3(a.c,"case ");a.fh(t,(_4(),W4));u4(a,H);break;case 69:Yd(p==1,b);L3(a.c,JNc);u4(a,t);break;case 83:Yd(p==2,b);if(t.n!=(Afc(),fec))throw gZ(new Hqc(VNc));a.fh(t,(_4(),W4));L3(a.c,":");H.n==Vcc||a.c.$g();A4(a,H,c==U4?U4:W4,true);break;case 92:if(a.i){L3(a.c,"(");a.fh(t,(_4(),W4));L3(a.c,")")}else a.fh(t,c);break;case 117:a.fh(t,(_4(),Z4));a.fh(t.f,W4);break;case 118:P3(a.c);for(i=t;i;i=i.f)if(i.n==(Afc(),jfc))r4(a,D4(i.Vh()));else{Q3(a.c);a.fh(i.c,(_4(),Z4));U3(a.c)}T3(a.c);break; +case 123:L3(a.c,$Kc);break;case 124:L3(a.c,XKc);break;case 125:L3(a.c,YKc);break;case 129:L3(a.c,"any");break;case 131:L3(a.c,XNc);break;case 133:a.fh(t,(_4(),W4));break;case 137:v4(a,t,arb((Afc(),Bcc)),c);L3(a.c,"[]");break;case 126:X=t;L3(a.c,"(");z4(a,t.f,true,(_4(),W4),",");L3(a.c,")");N3(a.c,"=>",true);a.fh(X,W4);break;case 128:z4(a,t,true,(_4(),W4),"|");break;case 135:L3(a.c,"{");z4(a,t,false,(_4(),W4),",");L3(a.c,"}");break;case 127:a.fh(t,(_4(),W4));L3(a.c,"<");z4(a,t.f,true,W4,",");L3(a.c, +">");break;case 139:L3(a.c,"<");z4(a,t,false,(_4(),$4),",");L3(a.c,">");break;case 138:x4(a,b.Wh());if(b.c){L3(a.c,NNc);a.c.$g();a.fh(b.c,(_4(),W4))}break;case 151:{Yd(p==3,b);J=t;bb=t.f;I=H;L3(a.c,YNc);a.fh(J,(_4(),W4));A=H8b(J,(n8b(),Y7b));!!A&&a.fh(A,W4);if(bb.n!=(Afc(),tdc)){L3(a.c,NNc);ab=bb.c;a.fh(ab,W4);while(ab=ab.f){L3(a.c,",");a.c.$g();a.fh(ab,W4)}}a.fh(I,W4)}break;case 154:{Yd(p==2,b);J=t;I=H;L3(a.c,ZNc);a.fh(J,(_4(),W4));a.fh(I,W4);break}case 162:{Yd(p==2,b);J=t;s=H;L3(a.c,"namespace"); +a.fh(J,(_4(),W4));a.fh(s,W4);break}case 157:L3(a.c,$Nc);r4(a,b.Wh());N3(a.c,"=",true);a.fh(H,(_4(),W4));a.c.Wg(true);break;case 158:L3(a.c,"declare");a.fh(t,(_4(),W4));I4(a,b,c);break;case 160:L3(a.c,"[");a.fh(t,(_4(),W4));L3(a.c,"]");G4(a,b);a.c.Wg(true);break;case 161:E8b(b,(n8b(),Q7b))!=0&&L3(a.c,"new ");w=H8b(b,Y7b);!!w&&a.fh(w,(_4(),W4));a.fh(t,(_4(),W4));G4(a,b);a.c.Wg(true);break;default:throw gZ(new GD("Unknown token "+cb+gMc+E9b(b)));}a.c.Vg(b)}function $2b(){var a;a=new stc;a.a+='{"externs/es3.js":"/*\\n * Copyright 2008 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview ECMAScript 3 Built-Ins. This include common extensions so this\\n * is actually ES3+Reality.\\n * @externs\\n * @author acleung@google.com (Alan Leung)\\n * @author stevey@google.com (Steve Yegge)\\n * @author nicksantos@google.com (Nick Santos)\\n * @author arv@google.com (Erik Arvidsson)\\n * @author johnlenz@google.com (John Lenz)\\n */\\n\\n\\n// START ES6 RETROFIT CODE\\n// symbol, Symbol and Symbol.iterator are actually ES6 types but some\\n// base types require them to be part of their definition (such as Array).\\n\\n\\n/**\\n * @constructor\\n * @param {*=} opt_description\\n * @return {symbol}\\n */\\nfunction Symbol(opt_description) {}\\n\\n\\n/**\\n * @const {string|undefined}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/description\\n */\\nSymbol.prototype.description;\\n\\n\\n/**\\n * @param {string} sym\\n * @return {symbol|undefined}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/for\\n */\\nSymbol.for;\\n\\n\\n/**\\n * @param {symbol} sym\\n * @return {string|undefined}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/keyFor\\n */\\nSymbol.keyFor;\\n\\n\\n// Well known symbols\\n\\n/** @const {symbol} */\\nSymbol.iterator;\\n\\n/** @const {symbol} */\\nSymbol.toStringTag;\\n\\n/** @const {symbol} */\\nSymbol.unscopables;\\n\\n\\n/**\\n * @record\\n * @template VALUE\\n */\\nfunction IIterableResult() {};\\n\\n/** @type {boolean} */\\nIIterableResult.prototype.done;\\n\\n/** @type {VALUE} */\\nIIterableResult.prototype.value;\\n\\n\\n\\n/**\\n * @interface\\n * @template VALUE\\n */\\nfunction Iterable() {}\\n\\n// TODO(johnlenz): remove the suppression when the compiler understands\\n// \\"symbol\\" natively\\n/**\\n * @return {!Iterator}\\n * @suppress {externsValidation}\\n */\\nIterable.prototype[Symbol.iterator] = function() {};\\n\\n\\n\\n/**\\n * @interface\\n * @template VALUE\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/The_Iterator_protocol\\n */\\nfunction Iterator() {}\\n\\n/**\\n * @param {?=} opt_value\\n * @return {!IIterableResult}\\n */\\nIterator.prototype.next;\\n\\n\\n/**\\n * Use this to indicate a type is both an Iterator and an Iterable.\\n * @interface\\n * @extends {Iterator}\\n * @extends {Iterable}\\n * @template T\\n */\\nfunction IteratorIterable() {}\\n\\n// END ES6 RETROFIT CODE\\n\\n\\n/**\\n * @interface\\n * @template KEY1, VALUE1\\n */\\nfunction IObject() {}\\n\\n/**\\n * @record\\n * @extends {IObject}\\n * @template VALUE2\\n */\\nfunction IArrayLike() {}\\n\\n/** @type {number} */\\nIArrayLike.prototype.length;\\n\\n/**\\n * @constructor\\n * @implements {IArrayLike}\\n * @implements {Iterable}\\n * @template T\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments\\n */\\nfunction Arguments() {}\\n\\n/**\\n * @type {Function}\\n * @see http://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Functions_and_function_scope/arguments/callee\\n */\\nArguments.prototype.callee;\\n\\n/**\\n * Use the non-standard {@see Function.prototype.caller} property of a function\\n * object instead.\\n * @type {Function}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments/caller\\n * @deprecated\\n */\\nArguments.prototype.caller;\\n\\n/**\\n * @type {number}\\n * @see http://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Functions_and_function_scope/arguments/length\\n */\\nArguments.prototype.length;\\n\\n/**\\n * Not actually a global variable, when running in a browser environment. But\\n * we need it in order for the type checker to typecheck the \\"arguments\\"\\n * variable in a function correctly.\\n *\\n * TODO(tbreisacher): There should be a separate \'arguments\' variable of type\\n * `Array`, in the d8 externs.\\n *\\n * @type {!Arguments}\\n * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Functions_and_function_scope/arguments\\n */\\nvar arguments;\\n\\n/**\\n * @type {number}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Infinity\\n * @const\\n */\\nvar Infinity;\\n\\n/**\\n * @type {number}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NaN\\n * @const\\n */\\nvar NaN;\\n\\n/**\\n * @type {undefined}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined\\n * @const\\n */\\nvar undefined;\\n\\n/**\\n * @param {string} uri\\n * @return {string}\\n * @throws {URIError} when used wrongly.\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURI\\n */\\nfunction decodeURI(uri) {}\\n\\n/**\\n * @param {string} uri\\n * @return {string}\\n * @throws {URIError} when used wrongly.\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent\\n */\\nfunction decodeURIComponent(uri) {}\\n\\n/**\\n * @param {string} uri\\n * @return {string}\\n * @throws {URIError} if one attempts to encode a surrogate which is not part of\\n * a high-low pair.\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI\\n */\\nfunction encodeURI(uri) {}\\n\\n/**\\n * @param {string} uri\\n * @return {string}\\n * @throws {URIError} if one attempts to encode a surrogate which is not part of\\n * a high-low pair.\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent\\n */\\nfunction encodeURIComponent(uri) {}\\n\\n/**\\n * Should only be used in browsers where encode/decodeURIComponent\\n * are not present, as the latter handle fancy Unicode characters.\\n * @param {string} str\\n * @return {string}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Predefined_Functions/escape_and_unescape_Functions\\n */\\nfunction escape(str) {}\\n\\n/**\\n * Should only be used in browsers where encode/decodeURIComponent\\n * are not present, as the latter handle fancy Unicode characters.\\n * @param {string} str\\n * @return {string}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Predefined_Functions/escape_and_unescape_Functions\\n */\\nfunction unescape(str) {}\\n\\n/**\\n * @param {*} num\\n * @return {boolean}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite\\n */\\nfunction isFinite(num) {}\\n\\n/**\\n * @param {*} num\\n * @return {boolean}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isNaN\\n */\\nfunction isNaN(num) {}\\n\\n/**\\n * @param {*} num\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseFloat\\n */\\nfunction parseFloat(num) {}\\n\\n/**\\n * Parse an integer. Use of `parseInt` without `base` is strictly\\n * banned in Google. If you really want to parse octal or hex based on the\\n * leader, then pass `undefined` as the base.\\n *\\n * @param {*} num\\n * @param {number|undefined} base\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt\\n */\\nfunction parseInt(num, base) {}\\n\\n/**\\n * @param {string} code\\n * @return {*}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval\\n */\\nfunction eval(code) {}\\n\\n\\n\\n/**\\n * @constructor\\n * @param {*=} opt_value\\n * @return {!Object}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object\\n */\\nfunction Object(opt_value) {}\\n\\n/**\\n * The constructor of the current object.\\n * @type {Function}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor\\n */\\nObject.prototype.constructor = function() {};\\n\\n/**\\n * Binds an object\'s property to a function to be called when that property is\\n * looked up.\\n * Mozilla-only.\\n *\\n * @param {string} sprop\\n * @param {Function} fun\\n * @modifies {this}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineGetter\\n * @return {undefined}\\n */\\nObject.prototype.__defineGetter__ = function(sprop, fun) {};\\n\\n/**\\n * Binds an object\'s property to a function to be called when an attempt is made\\n * to set that property.\\n * Mozilla-only.\\n *\\n * @param {string} sprop\\n * @param {Function} fun\\n * @modifies {this}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineSetter\\n * @return {undefined}\\n */\\nObject.prototype.__defineSetter__ = function(sprop, fun) {};\\n\\n/**\\n * Returns whether the object has a property with the specified name.\\n *\\n * @param {*} propertyName Implicitly cast to a string.\\n * @return {boolean}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty\\n */\\nObject.prototype.hasOwnProperty = function(propertyName) {};\\n\\n/**\\n * Returns whether an object exists in another object\'s prototype chain.\\n *\\n * @param {Object} other\\n * @return {boolean}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isPrototypeOf\\n */\\nObject.prototype.isPrototypeOf = function(other) {};\\n\\n/**\\n * Return the function bound as a getter to the specified property.\\n * Mozilla-only.\\n *\\n * @param {string} sprop a string containing the name of the property whose\\n * getter should be returned\\n * @return {Function}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/lookupGetter\\n */\\nObject.prototype.__lookupGetter__ = function(sprop) {};\\n\\n/**\\n * Return the function bound as a setter to the specified property.\\n * Mozilla-only.\\n *\\n * @param {string} sprop a string containing the name of the property whose\\n * setter should be returned.\\n * @return {Function}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/lookupSetter\\n */\\nObject.prototype.__lookupSetter__ = function(sprop) {};\\n\\n/**\\n * Executes a function when a non-existent method is called on an object.\\n * Mozilla-only.\\n *\\n * @param {Function} fun\\n * @return {*}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/noSuchMethod\\n */\\nObject.prototype.__noSuchMethod__ = function(fun) {};\\n\\n/**\\n * Points to an object\'s context. For top-level objects, this is the e.g. window.\\n * Mozilla-only.\\n *\\n * @type {Object}\\n * @deprecated\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/parent\\n */\\nObject.prototype.__parent__;\\n\\n/**\\n * Points to the object which was used as prototype when the object was instantiated.\\n * Mozilla-only.\\n *\\n * Will be null on Object.prototype.\\n *\\n * @type {Object}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto\\n */\\nObject.prototype.__proto__;\\n\\n/**\\n * Determine whether the specified property in an object can be enumerated by a\\n * for..in loop, with the exception of properties inherited through the\\n * prototype chain.\\n *\\n * @param {string} propertyName\\n * @return {boolean}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/propertyIsEnumerable\\n */\\nObject.prototype.propertyIsEnumerable = function(propertyName) {};\\n\\n/**\\n * Returns a localized string representing the object.\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toLocaleString\\n */\\nObject.prototype.toLocaleString = function() {};\\n\\n/**\\n * Returns a string representing the source code of the object.\\n * Mozilla-only.\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toSource\\n */\\nObject.prototype.toSource = function() {};\\n\\n/**\\n * Returns a string representing the object.\\n * @this {*}\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString\\n */\\nObject.prototype.toString = function() {};\\n\\n/**\\n * Returns the object\'s `this` value.\\n * @return {*}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf\\n */\\nObject.prototype.valueOf = function() {};\\n\\n/**\\n * @constructor\\n * @param {...*} var_args\\n * @throws {Error}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function\\n */\\nfunction Function(var_args) {}\\n\\n/**\\n * @param {...*} var_args\\n * @return {*}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call\\n */\\nFunction.prototype.call = function(var_args) {};\\n\\n/**\\n * @param {...*} var_args\\n * @return {*}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply\\n */\\nFunction.prototype.apply = function(var_args) {};\\n\\nFunction.prototype.arguments;\\n\\n/**\\n * @type {number}\\n * @deprecated\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/arity\\n */\\nFunction.prototype.arity;\\n\\n/**\\n * Nonstandard; Mozilla and JScript only.\\n * @type {Function}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller\\n */\\nFunction.prototype.caller;\\n\\n/**\\n * Nonstandard.\\n * @type {?}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/displayName\\n */\\nFunction.prototype.displayName;\\n\\n/**\\n * Expected number of arguments.\\n * @type {number}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/length\\n */\\nFunction.prototype.length;\\n\\n/**\\n * @type {string}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name\\n */\\nFunction.prototype.name;\\n\\n/**\\n * @this {Function}\\n * @return {string}\\n * @nosideeffects\\n * @override\\n */\\nFunction.prototype.toString = function() {};\\n\\n\\n/**\\n * @constructor\\n * @implements {IArrayLike}\\n * @implements {Iterable}\\n * @param {...*} var_args\\n * @return {!Array}\\n * @nosideeffects\\n * @template T\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array\\n */\\nfunction Array(var_args) {}\\n\\n/**\\n * @return {Iterator}\\n * @suppress {externsValidation}\\n */\\nArray.prototype[Symbol.iterator] = function() {};\\n\\n// Functions:\\n\\n/**\\n * Returns a new array comprised of this array joined with other array(s)\\n * and/or value(s).\\n *\\n * @param {...*} var_args\\n * @return {!Array}\\n * @this {*}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat\\n */\\nArray.prototype.concat = function(var_args) {};\\n\\n/**\\n * Joins all elements of an array into a string.\\n *\\n * @param {*=} opt_separator Specifies a string to separate each element of the\\n * array. The separator is converted to a string if necessary. If omitted,\\n * the array elements are separated with a comma.\\n * @return {string}\\n * @this {IArrayLike|string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join\\n */\\nArray.prototype.join = function(opt_separator) {};\\n\\n/**\\n * Removes the last element from an array and returns that element.\\n *\\n * @return {T}\\n * @this {IArra'; +a.a+='yLike}\\n * @modifies {this}\\n * @template T\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop\\n */\\nArray.prototype.pop = function() {};\\n\\n// TODO(bradfordcsmith): remove \\"undefined\\" from the var_args of push\\n/**\\n * Mutates an array by appending the given elements and returning the new\\n * length of the array.\\n *\\n * @param {...(T|undefined)} var_args\\n * @return {number} The new length of the array.\\n * @this {IArrayLike}\\n * @template T\\n * @modifies {this}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push\\n */\\nArray.prototype.push = function(var_args) {};\\n\\n/**\\n * Transposes the elements of an array in place: the first array element becomes the\\n * last and the last becomes the first. The mutated array is also returned.\\n *\\n * @return {THIS} A reference to the original modified array.\\n * @this {THIS}\\n * @template THIS\\n * @modifies {this}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse\\n */\\nArray.prototype.reverse = function() {};\\n\\n/**\\n * Removes the first element from an array and returns that element. This\\n * method changes the length of the array.\\n *\\n * @this {IArrayLike}\\n * @modifies {this}\\n * @return {T}\\n * @template T\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift\\n */\\nArray.prototype.shift = function() {};\\n\\n/**\\n * Extracts a section of an array and returns a new array.\\n *\\n * @param {*=} opt_begin Zero-based index at which to begin extraction. A\\n * non-number type will be auto-cast by the browser to a number.\\n * @param {*=} opt_end Zero-based index at which to end extraction. slice\\n * extracts up to but not including end.\\n * @return {!Array}\\n * @this {IArrayLike|string}\\n * @template T\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice\\n */\\nArray.prototype.slice = function(opt_begin, opt_end) {};\\n\\n/**\\n * Sorts the elements of an array in place.\\n *\\n * @param {function(T,T):number=} opt_compareFn Specifies a function that\\n * defines the sort order.\\n * @this {IArrayLike}\\n * @template T\\n * @modifies {this}\\n * @return {!Array}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort\\n */\\nArray.prototype.sort = function(opt_compareFn) {};\\n\\n/**\\n * Changes the content of an array, adding new elements while removing old\\n * elements.\\n *\\n * @param {*=} opt_index Index at which to start changing the array. If negative,\\n * will begin that many elements from the end. A non-number type will be\\n * auto-cast by the browser to a number.\\n * @param {*=} opt_howMany An integer indicating the number of old array elements\\n * to remove.\\n * @param {...T} var_args\\n * @return {!Array}\\n * @this {IArrayLike}\\n * @modifies {this}\\n * @template T\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice\\n */\\nArray.prototype.splice = function(opt_index, opt_howMany, var_args) {};\\n\\n/**\\n * @return {string}\\n * @this {Object}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toSource\\n */\\nArray.prototype.toSource;\\n\\n/**\\n * @this {Array}\\n * @return {string}\\n * @nosideeffects\\n * @override\\n */\\nArray.prototype.toString = function() {};\\n\\n/**\\n * Adds one or more elements to the beginning of an array and returns the new\\n * length of the array.\\n *\\n * @param {...*} var_args\\n * @return {number} The new length of the array\\n * @this {IArrayLike}\\n * @modifies {this}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift\\n */\\nArray.prototype.unshift = function(var_args) {};\\n\\n/**\\n * Apply a function simultaneously against two values of the array (from\\n * left-to-right) as to reduce it to a single value.\\n *\\n * @param {?function(?, T, number, !Array) : R} callback\\n * @param {*=} opt_initialValue\\n * @return {R}\\n * @this {IArrayLike|string}\\n * @template T,R\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce\\n */\\nArray.prototype.reduce = function(callback, opt_initialValue) {};\\n\\n/**\\n * Apply a function simultaneously against two values of the array (from\\n * right-to-left) as to reduce it to a single value.\\n *\\n * @param {?function(?, T, number, !Array) : R} callback\\n * @param {*=} opt_initialValue\\n * @return {R}\\n * @this {IArrayLike|string}\\n * @template T,R\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduceRight\\n */\\nArray.prototype.reduceRight = function(callback, opt_initialValue) {};\\n\\n/**\\n * Available in ECMAScript 5, Mozilla 1.6+.\\n * @param {?function(this:S, T, number, !Array): ?} callback\\n * @param {S=} opt_thisobj\\n * @return {boolean}\\n * @this {IArrayLike|string}\\n * @template T,S\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every\\n */\\nArray.prototype.every = function(callback, opt_thisobj) {};\\n\\n/**\\n * Available in ECMAScript 5, Mozilla 1.6+.\\n * @param {?function(this:S, T, number, !Array): ?} callback\\n * @param {S=} opt_thisobj\\n * @return {!Array}\\n * @this {IArrayLike|string}\\n * @template T,S\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter\\n */\\nArray.prototype.filter = function(callback, opt_thisobj) {};\\n\\n/**\\n * Available in ECMAScript 5, Mozilla 1.6+.\\n * @param {?function(this:S, T, number, !Array): ?} callback\\n * @param {S=} opt_thisobj\\n * @this {IArrayLike|string}\\n * @template T,S\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach\\n * @return {undefined}\\n */\\nArray.prototype.forEach = function(callback, opt_thisobj) {};\\n\\n/**\\n * Available in ECMAScript 5, Mozilla 1.6+.\\n * @param {T} obj\\n * @param {number=} opt_fromIndex\\n * @return {number}\\n * @this {IArrayLike|string}\\n * @nosideeffects\\n * @template T\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf\\n */\\nArray.prototype.indexOf = function(obj, opt_fromIndex) {};\\n\\n/**\\n * Available in ECMAScript 5, Mozilla 1.6+.\\n * @param {T} obj\\n * @param {number=} opt_fromIndex\\n * @return {number}\\n * @this {IArrayLike|string}\\n * @nosideeffects\\n * @template T\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf\\n */\\nArray.prototype.lastIndexOf = function(obj, opt_fromIndex) {};\\n\\n/**\\n * Available in ECMAScript 5, Mozilla 1.6+.\\n * @param {?function(this:S, T, number, !Array): R} callback\\n * @param {S=} opt_thisobj\\n * @return {!Array}\\n * @this {IArrayLike|string}\\n * @template T,S,R\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map\\n */\\nArray.prototype.map = function(callback, opt_thisobj) {};\\n\\n/**\\n * Available in ECMAScript 5, Mozilla 1.6+.\\n * @param {?function(this:S, T, number, !Array): ?} callback\\n * @param {S=} opt_thisobj\\n * @return {boolean}\\n * @this {IArrayLike|string}\\n * @template T,S\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some\\n */\\nArray.prototype.some = function(callback, opt_thisobj) {};\\n\\n/**\\n * @type {number}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/index\\n */\\nArray.prototype.index;\\n\\n/**\\n * @type {?string}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/input\\n */\\nArray.prototype.input;\\n\\n/**\\n * @type {number}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/length\\n */\\nArray.prototype.length;\\n\\n/**\\n * @param {IArrayLike} arr\\n * @param {?function(this:S, T, number, ?) : ?} callback\\n * @param {S=} opt_context\\n * @return {boolean}\\n * @template T,S\\n */\\nArray.every = function(arr, callback, opt_context) {};\\n\\n/**\\n * @param {IArrayLike} arr\\n * @param {?function(this:S, T, number, ?) : ?} callback\\n * @param {S=} opt_context\\n * @return {!Array}\\n * @template T,S\\n */\\nArray.filter = function(arr, callback, opt_context) {};\\n\\n/**\\n * @param {IArrayLike} arr\\n * @param {?function(this:S, T, number, ?) : ?} callback\\n * @param {S=} opt_context\\n * @template T,S\\n * @return {undefined}\\n */\\nArray.forEach = function(arr, callback, opt_context) {};\\n\\n/**\\n * Mozilla 1.6+ only.\\n * @param {IArrayLike} arr\\n * @param {T} obj\\n * @param {number=} opt_fromIndex\\n * @return {number}\\n * @template T\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf\\n */\\nArray.indexOf = function(arr, obj, opt_fromIndex) {};\\n\\n/**\\n * Mozilla 1.6+ only.\\n * @param {IArrayLike} arr\\n * @param {T} obj\\n * @param {number=} opt_fromIndex\\n * @return {number}\\n * @template T\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf\\n */\\nArray.lastIndexOf = function(arr, obj, opt_fromIndex) {};\\n\\n/**\\n * @param {IArrayLike} arr\\n * @param {?function(this:S, T, number, !Array): R} callback\\n * @param {S=} opt_context\\n * @return {!Array}\\n * @template T,S,R\\n */\\nArray.map = function(arr, callback, opt_context) {};\\n\\n/**\\n * @param {IArrayLike} arr\\n * @param {?function(this:S, T, number, ?) : ?} callback\\n * @param {S=} opt_context\\n * @return {boolean}\\n * @template T,S\\n */\\nArray.some = function(arr, callback, opt_context) {};\\n\\n/**\\n * Introduced in 1.8.5.\\n * @param {*} arr\\n * @return {boolean}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/isArray\\n */\\nArray.isArray = function(arr) {};\\n\\n/**\\n * @constructor\\n * @param {*=} opt_value\\n * @return {boolean}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean\\n */\\nfunction Boolean(opt_value) {}\\n\\n/**\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/toSource\\n * @override\\n */\\nBoolean.prototype.toSource = function() {};\\n\\n/**\\n * @this {boolean|Boolean}\\n * @return {string}\\n * @nosideeffects\\n * @override\\n */\\nBoolean.prototype.toString = function() {};\\n\\n/**\\n * @return {boolean}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/valueOf\\n * @override\\n */\\nBoolean.prototype.valueOf = function() {};\\n\\n/**\\n * @constructor\\n * @param {*=} opt_value\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number\\n */\\nfunction Number(opt_value) {}\\n\\n/**\\n * @this {Number|number}\\n * @param {number=} opt_fractionDigits\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toExponential\\n */\\nNumber.prototype.toExponential = function(opt_fractionDigits) {};\\n\\n/**\\n * @this {Number|number}\\n * @param {*=} opt_digits\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed\\n */\\nNumber.prototype.toFixed = function(opt_digits) {};\\n\\n/**\\n * @this {Number|number}\\n * @param {number=} opt_precision\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toPrecision\\n */\\nNumber.prototype.toPrecision = function(opt_precision) {};\\n\\n/**\\n * Returns a string representing the number.\\n * @this {Number|number}\\n * @param {(number|Number)=} opt_radix An optional radix.\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString\\n * @override\\n */\\nNumber.prototype.toString = function(opt_radix) {};\\n\\n// Properties.\\n/**\\n * @const {number}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_VALUE\\n */\\nNumber.MAX_VALUE;\\n\\n/**\\n * @const {number}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_VALUE\\n */\\nNumber.MIN_VALUE;\\n\\n/**\\n * @const {number}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/NaN\\n */\\nNumber.NaN;\\n\\n/**\\n * @const {number}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/NEGATIVE_INFINITY\\n */\\nNumber.NEGATIVE_INFINITY;\\n\\n/**\\n * @const {number}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/POSITIVE_INFINITY\\n */\\nNumber.POSITIVE_INFINITY;\\n\\n\\n/**\\n * @const\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math\\n */\\nvar Math = {};\\n\\n/**\\n * @param {?} x\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/abs\\n */\\nMath.abs = function(x) {};\\n\\n/**\\n * @param {?} x\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acos\\n */\\nMath.acos = function(x) {};\\n\\n/**\\n * @param {?} x\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asin\\n */\\nMath.asin = function(x) {};\\n\\n/**\\n * @param {?} x\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan\\n */\\nMath.atan = function(x) {};\\n\\n/**\\n * @param {?} y\\n * @param {?} x\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2\\n */\\nMath.atan2 = function(y, x) {};\\n\\n/**\\n * @param {?} x\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/ceil\\n */\\nMath.ceil = function(x) {};\\n\\n/**\\n * @param {?} x\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cos\\n */\\nMath.cos = function(x) {};\\n\\n/**\\n * @param {?} x\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/exp\\n */\\nMath.exp = function(x) {};\\n\\n/**\\n * @param {?} x\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor\\n */\\nMath.floor = function(x) {};\\n\\n/**\\n * @param {?} x\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log\\n */\\nMath.log = function(x) {};\\n\\n/**\\n * @param {...?} var_args\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max\\n */\\nMath.max = function(var_args) {};\\n\\n/**\\n * @param {...?} var_args\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/min\\n */\\nMath.min = function(var_args) {};\\n\\n/**\\n * @param {?} x\\n * @param {?} y\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/pow\\n */\\nMath.pow = function(x, y) {};\\n\\n/**\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random\\n */\\nMath.random = function() {};\\n\\n/**\\n * @param {?} x\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round\\n */\\nMath.round = function(x) {};\\n\\n/**\\n * @param {?} x\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sin'; +a.a+="\\n */\\nMath.sin = function(x) {};\\n\\n/**\\n * @param {?} x\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sqrt\\n */\\nMath.sqrt = function(x) {};\\n\\n/**\\n * @param {?} x\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tan\\n */\\nMath.tan = function(x) {};\\n\\n/**\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/toSource\\n */\\nMath.toSource = function() {};\\n\\n// Properties:\\n\\n/**\\n * @const {number}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/E\\n */\\nMath.E;\\n\\n/**\\n * @const {number}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/LN2\\n */\\nMath.LN2;\\n\\n/**\\n * @const {number}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/LN10\\n */\\nMath.LN10;\\n\\n/**\\n * @const {number}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/LOG2E\\n */\\nMath.LOG2E;\\n\\n/**\\n * @const {number}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/LOG10E\\n */\\nMath.LOG10E;\\n\\n/**\\n * @const {number}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/PI\\n */\\nMath.PI;\\n\\n/**\\n * @const {number}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/SQRT1_2\\n */\\nMath.SQRT1_2;\\n\\n/**\\n * @const {number}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/SQRT2\\n */\\nMath.SQRT2;\\n\\n\\n/**\\n * @param {?=} opt_yr_num\\n * @param {?=} opt_mo_num\\n * @param {?=} opt_day_num\\n * @param {?=} opt_hr_num\\n * @param {?=} opt_min_num\\n * @param {?=} opt_sec_num\\n * @param {?=} opt_ms_num\\n * @constructor\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date\\n */\\nfunction Date(opt_yr_num, opt_mo_num, opt_day_num, opt_hr_num, opt_min_num,\\n opt_sec_num, opt_ms_num) {}\\n\\n/**\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now\\n */\\nDate.now = function() {};\\n\\n/**\\n * Parses a string representation of a date, and returns the number\\n * of milliseconds since January 1, 1970, 00:00:00, local time.\\n * @param {*} date\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse\\n */\\nDate.parse = function(date) {};\\n\\n/**\\n * @param {number} year\\n * @param {number} month\\n * @param {number=} opt_date\\n * @param {number=} opt_hours\\n * @param {number=} opt_minute\\n * @param {number=} opt_second\\n * @param {number=} opt_ms\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/UTC\\n */\\nDate.UTC = function(year, month,\\n opt_date, opt_hours, opt_minute, opt_second, opt_ms) {};\\n\\n/**\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getDate\\n */\\nDate.prototype.getDate = function() {};\\n\\n/**\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getDay\\n */\\nDate.prototype.getDay = function() {};\\n\\n/**\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getMonth\\n */\\nDate.prototype.getMonth = function() {};\\n\\n/**\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getFullYear\\n */\\nDate.prototype.getFullYear = function() {};\\n\\n/**\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getYear\\n */\\nDate.prototype.getYear = function() {};\\n\\n/**\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getHours\\n */\\nDate.prototype.getHours = function() {};\\n\\n/**\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getMinutes\\n */\\nDate.prototype.getMinutes = function() {};\\n\\n/**\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getSeconds\\n */\\nDate.prototype.getSeconds = function() {};\\n\\n/**\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getMilliseconds\\n */\\nDate.prototype.getMilliseconds = function() {};\\n\\n/**\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTime\\n */\\nDate.prototype.getTime = function() {};\\n\\n/**\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset\\n */\\nDate.prototype.getTimezoneOffset = function() {};\\n\\n/**\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCDate\\n */\\nDate.prototype.getUTCDate = function() {};\\n\\n/**\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCDay\\n */\\nDate.prototype.getUTCDay = function() {};\\n\\n/**\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCMonth\\n */\\nDate.prototype.getUTCMonth = function() {};\\n\\n/**\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCFullYear\\n */\\nDate.prototype.getUTCFullYear = function() {};\\n\\n/**\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCHours\\n */\\nDate.prototype.getUTCHours = function() {};\\n\\n/**\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCMinutes\\n */\\nDate.prototype.getUTCMinutes = function() {};\\n\\n/**\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCSeconds\\n */\\nDate.prototype.getUTCSeconds = function() {};\\n\\n/**\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCMilliseconds\\n */\\nDate.prototype.getUTCMilliseconds = function() {};\\n\\n/**\\n * Sets the day of the month for a specified date according to local time.\\n *\\n * @param {number} dayValue\\n * @modifies {this}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setDate\\n * @return {number}\\n */\\nDate.prototype.setDate = function(dayValue) {};\\n\\n/**\\n * Set the month for a specified date according to local time.\\n *\\n * @param {number} monthValue\\n * @param {number=} opt_dayValue\\n * @modifies {this}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setMonth\\n * @return {number}\\n */\\nDate.prototype.setMonth = function(monthValue, opt_dayValue) {};\\n\\n/**\\n * Sets the full year for a specified date according to local time.\\n *\\n * @param {number} yearValue\\n * @param {number=} opt_monthValue\\n * @param {number=} opt_dayValue\\n * @modifies {this}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setFullYear\\n * @return {number}\\n */\\nDate.prototype.setFullYear =\\n function(yearValue, opt_monthValue, opt_dayValue) {};\\n\\n/**\\n * Sets the year for a specified date according to local time.\\n *\\n * @param {number} yearValue\\n * @deprecated\\n * @modifies {this}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setYear\\n * @return {number}\\n */\\nDate.prototype.setYear = function(yearValue) {};\\n\\n/**\\n * Sets the hours for a specified date according to local time.\\n *\\n * @param {number} hoursValue\\n * @param {number=} opt_minutesValue\\n * @param {number=} opt_secondsValue\\n * @param {number=} opt_msValue\\n * @modifies {this}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setHours\\n * @return {number}\\n */\\nDate.prototype.setHours = function(hoursValue, opt_minutesValue,\\n opt_secondsValue, opt_msValue) {};\\n\\n/**\\n * Sets the minutes for a specified date according to local time.\\n *\\n * @param {number} minutesValue\\n * @param {number=} opt_secondsValue\\n * @param {number=} opt_msValue\\n * @modifies {this}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setMinutes\\n * @return {number}\\n */\\nDate.prototype.setMinutes =\\n function(minutesValue, opt_secondsValue, opt_msValue) {};\\n\\n/**\\n * Sets the seconds for a specified date according to local time.\\n *\\n * @param {number} secondsValue\\n * @param {number=} opt_msValue\\n * @modifies {this}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setSeconds\\n * @return {number}\\n */\\nDate.prototype.setSeconds = function(secondsValue, opt_msValue) {};\\n\\n/**\\n * Sets the milliseconds for a specified date according to local time.\\n *\\n * @param {number} millisecondsValue\\n * @modifies {this}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setMilliseconds\\n * @return {number}\\n */\\nDate.prototype.setMilliseconds = function(millisecondsValue) {};\\n\\n/**\\n * Sets the Date object to the time represented by a number of milliseconds\\n * since January 1, 1970, 00:00:00 UTC.\\n *\\n * @param {number} timeValue\\n * @modifies {this}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setTime\\n * @return {number}\\n */\\nDate.prototype.setTime = function(timeValue) {};\\n\\n/**\\n * Sets the day of the month for a specified date according to universal time.\\n *\\n * @param {number} dayValue\\n * @modifies {this}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setUTCDate\\n * @return {number}\\n */\\nDate.prototype.setUTCDate = function(dayValue) {};\\n\\n/**\\n * Sets the month for a specified date according to universal time.\\n *\\n * @param {number} monthValue\\n * @param {number=} opt_dayValue\\n * @modifies {this}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setUTCMonth\\n * @return {number}\\n */\\nDate.prototype.setUTCMonth = function(monthValue, opt_dayValue) {};\\n\\n/**\\n * Sets the full year for a specified date according to universal time.\\n *\\n * @param {number} yearValue\\n * @param {number=} opt_monthValue\\n * @param {number=} opt_dayValue\\n * @modifies {this}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setUTCFullYear\\n * @return {number}\\n */\\nDate.prototype.setUTCFullYear = function(yearValue, opt_monthValue,\\n opt_dayValue) {};\\n\\n/**\\n * Sets the hour for a specified date according to universal time.\\n *\\n * @param {number} hoursValue\\n * @param {number=} opt_minutesValue\\n * @param {number=} opt_secondsValue\\n * @param {number=} opt_msValue\\n * @modifies {this}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setUTCHours\\n * @return {number}\\n */\\nDate.prototype.setUTCHours = function(hoursValue, opt_minutesValue,\\n opt_secondsValue, opt_msValue) {};\\n\\n/**\\n * Sets the minutes for a specified date according to universal time.\\n *\\n * @param {number} minutesValue\\n * @param {number=} opt_secondsValue\\n * @param {number=} opt_msValue\\n * @modifies {this}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setUTCMinutes\\n * @return {number}\\n */\\nDate.prototype.setUTCMinutes = function(minutesValue, opt_secondsValue,\\n opt_msValue) {};\\n\\n\\n/**\\n * Sets the seconds for a specified date according to universal time.\\n *\\n * @param {number} secondsValue\\n * @param {number=} opt_msValue\\n * @modifies {this}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setUTCSeconds\\n * @return {number}\\n */\\nDate.prototype.setUTCSeconds = function(secondsValue, opt_msValue) {};\\n\\n/**\\n * Sets the milliseconds for a specified date according to universal time.\\n *\\n * @param {number} millisecondsValue\\n * @modifies {this}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setUTCMilliseconds\\n * @return {number}\\n */\\nDate.prototype.setUTCMilliseconds = function(millisecondsValue) {};\\n\\n/**\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toSource\\n * @override\\n */\\nDate.prototype.toSource = function() {};\\n\\n/**\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/toDateString\\n */\\nDate.prototype.toDateString = function() {};\\n\\n/**\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toGMTString\\n */\\nDate.prototype.toGMTString = function() {};\\n\\n/**\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toTimeString\\n */\\nDate.prototype.toTimeString = function() {};\\n\\n/**\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toUTCString\\n */\\nDate.prototype.toUTCString = function() {};\\n\\n/**\\n * @param {(string|Array)=} opt_locales\\n * @param {Object=} opt_options\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleDateString\\n */\\nDate.prototype.toLocaleDateString = function(opt_locales, opt_options) {};\\n\\n/**\\n * @param {string} formatString\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleFormat\\n */\\nDate.prototype.toLocaleFormat = function(formatString) {};\\n\\n/**\\n * @param {string|Array=} opt_locales\\n * @param {Object=} opt_options\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString\\n * @see http://www.ecma-international.org/ecma-402/1.0/#sec-13.3.1\\n * @override\\n */\\nDate.prototype.toLocaleString = function(opt_locales, opt_options) {};\\n\\n/**\\n * @param {(string|Array)=} opt_locales\\n * @param {Object=} opt_options\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleTimeString\\n */\\nDate.prototype.toLocaleTimeString = function(opt_locales, opt_options) {};\\n\\n/**\\n * @this {Date}\\n * @return {string}\\n * @nosideeffects\\n * @override\\n */\\nDate.prototype.toString = function() {};\\n\\n/**\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/valueOf\\n */\\nDate.prototype.valueOf;\\n\\n/**\\n * @constructor\\n * @implements {Iterable}\\n * @param {*=} opt_str\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String\\n */\\nfunction String(opt_str) {}\\n\\n/**\\n * @param {...number} var_args\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Gl"; +a.a+="obal_Objects/String/fromCharCode\\n */\\nString.fromCharCode = function(var_args) {};\\n\\n/**\\n * @this {String|string}\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/anchor\\n */\\nString.prototype.anchor = function() {};\\n\\n/**\\n * @this {String|string}\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/big\\n */\\nString.prototype.big = function() {};\\n\\n/**\\n * @this {String|string}\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/blink\\n */\\nString.prototype.blink = function() {};\\n\\n/**\\n * @this {String|string}\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/bold\\n */\\nString.prototype.bold = function() {};\\n\\n/**\\n * Returns the specified character from a string.\\n *\\n * @this {String|string}\\n * @param {number} index\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charAt\\n */\\nString.prototype.charAt = function(index) {};\\n\\n/**\\n * Returns a number indicating the Unicode value of the character at the given\\n * index.\\n *\\n * @this {String|string}\\n * @param {number=} opt_index\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt\\n */\\nString.prototype.charCodeAt = function(opt_index) {};\\n\\n/**\\n * Combines the text of two or more strings and returns a new string.\\n *\\n * @this {String|string}\\n * @param {...*} var_args\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/concat\\n */\\nString.prototype.concat = function(var_args) {};\\n\\n/**\\n * @this {String|string}\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fixed\\n */\\nString.prototype.fixed = function() {};\\n\\n/**\\n * @this {String|string}\\n * @param {string} color\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fontcolor\\n */\\nString.prototype.fontcolor = function(color) {};\\n\\n/**\\n * @this {String|string}\\n * @param {number} size\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fontsize\\n */\\nString.prototype.fontsize = function(size) {};\\n\\n/**\\n * Returns the index within the calling String object of the first occurrence\\n * of the specified value, starting the search at fromIndex, returns -1 if the\\n * value is not found.\\n *\\n * @this {String|string}\\n * @param {string|null} searchValue\\n * @param {(number|null)=} opt_fromIndex\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf\\n */\\nString.prototype.indexOf = function(searchValue, opt_fromIndex) {};\\n\\n/**\\n * @this {String|string}\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/italics\\n */\\nString.prototype.italics = function() {};\\n\\n/**\\n * Returns the index within the calling String object of the last occurrence of\\n * the specified value, or -1 if not found. The calling string is searched\\n * backward, starting at fromIndex.\\n *\\n * @this {String|string}\\n * @param {string|null} searchValue\\n * @param {(number|null)=} opt_fromIndex\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf\\n */\\nString.prototype.lastIndexOf = function(searchValue, opt_fromIndex) {};\\n\\n/**\\n * @this {String|string}\\n * @param {string} hrefAttribute\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/link\\n */\\nString.prototype.link = function(hrefAttribute) {};\\n\\n/**\\n * Returns a number indicating whether a reference string comes before or after\\n * or is the same as the given string in sort order.\\n *\\n * @this {*}\\n * @param {?string} compareString\\n * @param {string|Array=} locales\\n * @param {Object=} options\\n * @return {number}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/String/localeCompare\\n * @see http://www.ecma-international.org/ecma-402/1.0/#sec-13.1.1\\n */\\nString.prototype.localeCompare = function(compareString, locales, options) {};\\n\\n/**\\n * Used to retrieve the matches when matching a string against a regular\\n * expression.\\n *\\n * @this {String|string}\\n * @param {*} regexp\\n * @return {Array} This should really return an Array with a few\\n * special properties, but we do not have a good way to model this in\\n * our type system. Also see Regexp.prototype.exec.\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match\\n */\\nString.prototype.match = function(regexp) {};\\n\\n/**\\n * @this {String|string}\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/quote\\n */\\nString.prototype.quote = function() {};\\n\\n/**\\n * Finds a match between a regular expression and a string, and replaces the\\n * matched substring with a new substring.\\n *\\n * This may have side-effects if the replacement function has side-effects.\\n *\\n * @this {String|string}\\n * @param {RegExp|string} pattern\\n * @param {string|Function} replacement\\n * @return {string}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace\\n */\\nString.prototype.replace = function(pattern, replacement) {};\\n\\n/**\\n * Executes the search for a match between a regular expression and this String\\n * object.\\n *\\n * @this {String|string}\\n * @param {RegExp|string} pattern\\n * @return {number}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/search\\n */\\nString.prototype.search = function(pattern) {};\\n\\n/**\\n * @this {String|string}\\n * @param {number} begin\\n * @param {number=} opt_end\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice\\n */\\nString.prototype.slice = function(begin, opt_end) {};\\n\\n/**\\n * @this {String|string}\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/small\\n */\\nString.prototype.small = function() {};\\n\\n/**\\n * @this {String|string}\\n * @param {*=} opt_separator\\n * @param {number=} opt_limit\\n * @return {!Array}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split\\n */\\nString.prototype.split = function(opt_separator, opt_limit) {};\\n\\n/**\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/strike\\n */\\nString.prototype.strike = function() {};\\n\\n/**\\n * @this {String|string}\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/sub\\n */\\nString.prototype.sub = function() {};\\n\\n/**\\n * @this {String|string}\\n * @param {number} start\\n * @param {number=} opt_length\\n * @return {string} The specified substring.\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr\\n */\\nString.prototype.substr = function(start, opt_length) {};\\n\\n/**\\n * @this {String|string}\\n * @param {number} start\\n * @param {number=} opt_end\\n * @return {string} The specified substring.\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substring\\n */\\nString.prototype.substring = function(start, opt_end) {};\\n\\n/**\\n * @this {String|string}\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/sup\\n */\\nString.prototype.sup = function() {};\\n\\n/**\\n * @this {String|string}\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLocaleUpperCase\\n */\\nString.prototype.toLocaleUpperCase = function() {};\\n\\n/**\\n * @this {String|string}\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLocaleLowerCase\\n */\\nString.prototype.toLocaleLowerCase = function() {};\\n\\n/**\\n * @this {String|string}\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase\\n */\\nString.prototype.toLowerCase = function() {};\\n\\n/**\\n * @this {String|string}\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase\\n */\\nString.prototype.toUpperCase = function() {};\\n\\n/**\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toSource\\n * @override\\n */\\nString.prototype.toSource = function() {};\\n\\n/**\\n * @this {string|String}\\n * @return {string}\\n * @nosideeffects\\n * @override\\n */\\nString.prototype.toString = function() {};\\n\\n/**\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/valueOf\\n */\\nString.prototype.valueOf;\\n\\n/**\\n * @type {number}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/length\\n */\\nString.prototype.length;\\n\\n/**\\n * @constructor\\n * @param {*=} opt_pattern\\n * @param {*=} opt_flags\\n * @return {!RegExp}\\n * @throws {SyntaxError} if opt_pattern is an invalid pattern.\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp\\n */\\nfunction RegExp(opt_pattern, opt_flags) {}\\n\\n/**\\n * @param {*} pattern\\n * @param {*=} opt_flags\\n * @return {void}\\n * @modifies {this}\\n * @deprecated\\n * @see http://msdn.microsoft.com/en-us/library/x9cswe0z(v=VS.85).aspx\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/compile\\n */\\nRegExp.prototype.compile = function(pattern, opt_flags) {};\\n\\n/**\\n * @param {*} str The string to search.\\n * @return {?RegExpResult}\\n * @see http://msdn.microsoft.com/en-us/library/z908hy33(VS.85).aspx\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec\\n */\\nRegExp.prototype.exec = function(str) {};\\n\\n/**\\n * @param {*} str The string to search.\\n * @return {boolean} Whether the string was matched.\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test\\n */\\nRegExp.prototype.test = function(str) {};\\n\\n/**\\n * @this {RegExp}\\n * @return {string}\\n * @nosideeffects\\n * @override\\n */\\nRegExp.prototype.toString = function() {};\\n\\n/**\\n * @constructor\\n * @extends {Array}\\n */\\nvar RegExpResult = function() {};\\n\\n\\n/** @type {number} */\\nRegExpResult.prototype.index;\\n\\n\\n/** @type {string} */\\nRegExpResult.prototype.input;\\n\\n\\n/** @type {number} */\\nRegExpResult.prototype.length;\\n\\n\\n/**\\n * Not actually part of ES3; was added in 2018.\\n * https://github.com/tc39/proposal-regexp-named-groups\\n *\\n * @type {!Object}\\n */\\nRegExpResult.prototype.groups;\\n\\n\\n// Constructor properties:\\n\\n/**\\n * The string against which the last regexp was matched.\\n * @type {string}\\n * @see http://www.devguru.com/Technologies/Ecmascript/Quickref/regexp_input.html\\n */\\nRegExp.input;\\n\\n/**\\n * The last matched characters.\\n * @type {string}\\n * @see http://www.devguru.com/Technologies/Ecmascript/Quickref/regexp_lastMatch.html\\n */\\nRegExp.lastMatch;\\n\\n/**\\n * The last matched parenthesized substring, if any.\\n * @type {string}\\n * @see http://www.devguru.com/Technologies/Ecmascript/Quickref/regexp_lastParen.html\\n */\\nRegExp.lastParen;\\n\\n/**\\n * The substring of the input up to the characters most recently matched.\\n * @type {string}\\n * @see http://www.devguru.com/Technologies/Ecmascript/Quickref/regexp_leftContext.html\\n */\\nRegExp.leftContext;\\n\\n/**\\n * The substring of the input after the characters most recently matched.\\n * @type {string}\\n * @see http://www.devguru.com/Technologies/Ecmascript/Quickref/regexp_rightContext.html\\n */\\nRegExp.rightContext;\\n\\n/**\\n * @type {string}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp\\n */\\nRegExp.$1;\\n/**\\n * @type {string}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp\\n */\\nRegExp.$2;\\n/**\\n * @type {string}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp\\n */\\nRegExp.$3;\\n/**\\n * @type {string}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp\\n */\\nRegExp.$4;\\n/**\\n * @type {string}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp\\n */\\nRegExp.$5;\\n/**\\n * @type {string}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp\\n */\\nRegExp.$6;\\n/**\\n * @type {string}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp\\n */\\nRegExp.$7;\\n/**\\n * @type {string}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp\\n */\\nRegExp.$8;\\n/**\\n * @type {string}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp\\n */\\nRegExp.$9;\\n\\n// Prototype properties:\\n\\n/**\\n * Whether to test the regular expression against all possible matches\\n * in a string, or only against the first.\\n * @type {boolean}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/global\\n */\\nRegExp.prototype.global;\\n\\n/**\\n * Whether to ignore case while attempting a match in a string.\\n * @type {boolean}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/ignoreCase\\n */\\nRegExp.prototype.ignoreCase;\\n\\n/**\\n * The index at which to start the next match.\\n * @type {number}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex\\n */\\nRegExp.prototype.lastIndex;\\n\\n/**\\n * Whether or not the regular expression uses lastIndex.\\n * @type {boolean}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/sticky\\n */\\nRegExp.prototype.sticky;\\n\\n/**\\n * Whether or not to search in strings across multiple lines.\\n * @type {boolean}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/multiline\\n */\\nRegExp.prototype.multiline;\\n\\n/**\\n * The text of the pattern.\\n * @type {string}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/source\\n */\\nRegExp.prototype.source;\\n\\n/**\\n * The flags the regex was created with.\\n * @type {string}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/flags\\n */\\nRegExp.prototype.flags;\\n\\n/**\\n * @constructor\\n * @param {*=} opt_message\\n * @param {*=} opt_file\\n * @param {*=} opt_line\\n * @return {!Error}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error\\n */\\nfunction Error(opt_message, opt_file, opt_line) {}\\n\\n\\n/**\\n * Chrome/v8 specific, altering the maximum depth of the stack trace\\n * (10 by default).\\n * @type {number}\\n * @see http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi\\n */\\nError.stackTraceLimit;\\n\\n\\n/**\\n * Chrome/v8 specific, adds a stack trace to the error object. The optional\\n *"; +a.a+=' constructorOpt parameter allows you to pass in a function value. When\\n * collecting the stack trace all frames above the topmost call to this\\n * function, including that call, will be left out of the stack trace.\\n * @param {Object} error The object to add the stack trace to.\\n * @param {Function=} opt_constructor A function in the stack trace\\n * @see http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi\\n * @return {undefined}\\n */\\nError.captureStackTrace = function(error, opt_constructor){};\\n\\n\\n/**\\n * IE-only.\\n * @type {string}\\n * @see http://msdn.microsoft.com/en-us/library/2w6a45b5.aspx\\n */\\nError.prototype.description;\\n\\n\\n/**\\n * Mozilla-only.\\n * @type {number}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/lineNumber\\n */\\nError.prototype.lineNumber;\\n\\n/**\\n * Mozilla-only\\n * @type {string}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/fileName\\n */\\nError.prototype.fileName;\\n\\n/**\\n * @type {string}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/name\\n */\\nError.prototype.name;\\n\\n/**\\n * @type {string}\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/message\\n */\\nError.prototype.message;\\n\\n/**\\n * Doesn\'t seem to exist, but closure/debug.js references it.\\n */\\nError.prototype.sourceURL;\\n\\n/** @type {string} */\\nError.prototype.stack;\\n\\n\\n/**\\n * @constructor\\n * @extends {Error}\\n * @param {*=} opt_message\\n * @param {*=} opt_file\\n * @param {*=} opt_line\\n * @return {!EvalError}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/EvalError\\n */\\nfunction EvalError(opt_message, opt_file, opt_line) {}\\n\\n/**\\n * @constructor\\n * @extends {Error}\\n * @param {*=} opt_message\\n * @param {*=} opt_file\\n * @param {*=} opt_line\\n * @return {!RangeError}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RangeError\\n */\\nfunction RangeError(opt_message, opt_file, opt_line) {}\\n\\n/**\\n * @constructor\\n * @extends {Error}\\n * @param {*=} opt_message\\n * @param {*=} opt_file\\n * @param {*=} opt_line\\n * @return {!ReferenceError}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ReferenceError\\n */\\nfunction ReferenceError(opt_message, opt_file, opt_line) {}\\n\\n/**\\n * @constructor\\n * @extends {Error}\\n * @param {*=} opt_message\\n * @param {*=} opt_file\\n * @param {*=} opt_line\\n * @return {!SyntaxError}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError\\n */\\nfunction SyntaxError(opt_message, opt_file, opt_line) {}\\n\\n/**\\n * @constructor\\n * @extends {Error}\\n * @param {*=} opt_message\\n * @param {*=} opt_file\\n * @param {*=} opt_line\\n * @return {!TypeError}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError\\n */\\nfunction TypeError(opt_message, opt_file, opt_line) {}\\n\\n/**\\n * @constructor\\n * @extends {Error}\\n * @param {*=} opt_message\\n * @param {*=} opt_file\\n * @param {*=} opt_line\\n * @return {!URIError}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/URIError\\n */\\nfunction URIError(opt_message, opt_file, opt_line) {}\\n\\n\\n// JScript extensions.\\n// @see http://msdn.microsoft.com/en-us/library/894hfyb4(VS.80).aspx\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/7sw4ddf8.aspx\\n * @type {function(new:?, string, string=)}\\n */\\nfunction ActiveXObject(progId, opt_location) {}\\n","externs/es5.js":"/*\\n * Copyright 2009 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for ECMAScript 5.\\n * @see https://es5.github.io/\\n * @externs\\n * @author djlee@google.com (DJ Lee)\\n */\\n\\n\\n/**\\n * @param {Object|undefined} selfObj Specifies the object to which |this| should\\n * point when the function is run. If the value is null or undefined, it\\n * will default to the global object.\\n * @param {...*} var_args Additional arguments that are partially\\n * applied to fn.\\n * @return {!Function} A partially-applied form of the Function on which\\n * bind() was invoked as a method.\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind\\n */\\nFunction.prototype.bind = function(selfObj, var_args) {};\\n\\n\\n/**\\n * @this {String|string}\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/Trim\\n */\\nString.prototype.trim = function() {};\\n\\n\\n/**\\n * @this {String|string}\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/TrimLeft\\n */\\nString.prototype.trimLeft = function() {};\\n\\n\\n/**\\n * @this {String|string}\\n * @return {string}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/TrimRight\\n */\\nString.prototype.trimRight = function() {};\\n\\n\\n/**\\n * A object property descriptor used by Object.create, Object.defineProperty,\\n * Object.defineProperties, Object.getOwnPropertyDescriptor.\\n *\\n * @record\\n * @template THIS\\n */\\nfunction ObjectPropertyDescriptor() {}\\n\\n/** @type {(*|undefined)} */\\nObjectPropertyDescriptor.prototype.value;\\n\\n/** @type {(function(this: THIS):?)|undefined} */\\nObjectPropertyDescriptor.prototype.get;\\n\\n/** @type {(function(this: THIS, ?):void)|undefined} */\\nObjectPropertyDescriptor.prototype.set;\\n\\n/** @type {boolean|undefined} */\\nObjectPropertyDescriptor.prototype.writable;\\n\\n/** @type {boolean|undefined} */\\nObjectPropertyDescriptor.prototype.enumerable;\\n\\n/** @type {boolean|undefined} */\\nObjectPropertyDescriptor.prototype.configurable;\\n\\n\\n/**\\n * @param {?Object} proto\\n * @param {?Object=} opt_properties A map of ObjectPropertyDescriptors.\\n * @return {!Object}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/create\\n */\\nObject.create = function(proto, opt_properties) {};\\n\\n\\n/**\\n * @param {!Object} obj\\n * @param {string|symbol} prop\\n * @param {!Object} descriptor A ObjectPropertyDescriptor.\\n * @return {!Object}\\n * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty\\n */\\nObject.defineProperty = function(obj, prop, descriptor) {};\\n\\n\\n/**\\n * @param {!Object} obj\\n * @param {!Object} props A map of ObjectPropertyDescriptors.\\n * @return {!Object}\\n * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperties\\n */\\nObject.defineProperties = function(obj, props) {};\\n\\n\\n/**\\n * @param {T} obj\\n * @param {string|symbol} prop\\n * @return {!ObjectPropertyDescriptor|undefined}\\n * @nosideeffects\\n * @template T\\n * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor\\n */\\nObject.getOwnPropertyDescriptor = function(obj, prop) {};\\n\\n\\n/**\\n * @param {!Object} obj\\n * @return {!Array}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/keys\\n */\\nObject.keys = function(obj) {};\\n\\n\\n/**\\n * @param {!Object} obj\\n * @return {!Array}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames\\n */\\nObject.getOwnPropertyNames = function(obj) {};\\n\\n\\n/**\\n * @param {!Object} obj\\n * @return {Object}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/GetPrototypeOf\\n */\\nObject.getPrototypeOf = function(obj) {};\\n\\n\\n/**\\n * @param {T} obj\\n * @return {T}\\n * @template T\\n * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/preventExtensions\\n */\\nObject.preventExtensions = function(obj) {};\\n\\n\\n/**\\n * @param {T} obj\\n * @return {T}\\n * @template T\\n * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/seal\\n */\\nObject.seal = function(obj) {};\\n\\n\\n/**\\n * @param {T} obj\\n * @return {T}\\n * @template T\\n * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/freeze\\n */\\nObject.freeze = function(obj) {};\\n\\n\\n/**\\n * @param {!Object} obj\\n * @return {boolean}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/isExtensible\\n */\\nObject.isExtensible = function(obj) {};\\n\\n\\n/**\\n * @param {!Object} obj\\n * @return {boolean}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/isSealed\\n */\\nObject.isSealed = function(obj) {};\\n\\n\\n/**\\n * @param {!Object} obj\\n * @return {boolean}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/isFrozen\\n */\\nObject.isFrozen = function(obj) {};\\n\\n\\n/**\\n * @param {string=} opt_key The JSON key for this object.\\n * @return {*} The serializable representation of this object. Note that this\\n * need not be a string. See http://goo.gl/PEUvs.\\n * @see https://es5.github.io/#x15.12.3\\n */\\nObject.prototype.toJSON = function(opt_key) {};\\n\\n\\n/**\\n * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/toISOString\\n * @return {string}\\n */\\nDate.prototype.toISOString = function() {};\\n\\n\\n/**\\n * @param {*=} opt_ignoredKey\\n * @return {string}\\n * @override\\n */\\nDate.prototype.toJSON = function(opt_ignoredKey) {};\\n\\n\\n/**\\n * A fake type to model the JSON object.\\n * @constructor\\n */\\nfunction JSONType() {}\\n\\n\\n/**\\n * @param {string} jsonStr The string to parse.\\n * @param {(function(this:?, string, *) : *)=} opt_reviver\\n * @return {*} The JSON object.\\n * @throws {Error}\\n */\\nJSONType.prototype.parse = function(jsonStr, opt_reviver) {};\\n\\n\\n/**\\n * @param {*} jsonObj Input object.\\n * @param {(Array|(function(this:?, string, *) : *)|null)=} opt_replacer\\n * @param {(number|string)=} opt_space\\n * @return {string} JSON string which represents jsonObj.\\n * @throws {Error}\\n */\\nJSONType.prototype.stringify = function(jsonObj, opt_replacer, opt_space) {};\\n\\n\\n/**\\n * @type {!JSONType}\\n * @suppress {duplicate}\\n */\\nvar JSON;\\n","externs/es6.js":"/*\\n * Copyright 2014 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for ECMAScript 6 and later.\\n * @see https://tc39.github.io/ecma262/\\n * @see https://www.khronos.org/registry/typedarray/specs/latest/\\n * @externs\\n */\\n\\n\\n\\n/**\\n * @interface\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator\\n * @extends {IteratorIterable}\\n * @template VALUE\\n */\\nfunction Generator() {}\\n\\n/**\\n * @param {?=} opt_value\\n * @return {!IIterableResult}\\n * @override\\n */\\nGenerator.prototype.next = function(opt_value) {};\\n\\n/**\\n * @param {VALUE} value\\n * @return {!IIterableResult}\\n */\\nGenerator.prototype.return = function(value) {};\\n\\n/**\\n * @param {?} exception\\n * @return {!IIterableResult}\\n */\\nGenerator.prototype.throw = function(exception) {};\\n\\n\\n\\n/**\\n * @param {number} value\\n * @return {number}\\n * @nosideeffects\\n */\\nMath.log10 = function(value) {};\\n\\n/**\\n * @param {number} value\\n * @return {number}\\n * @nosideeffects\\n */\\nMath.log2 = function(value) {};\\n\\n/**\\n * @param {number} value\\n * @return {number}\\n * @nosideeffects\\n */\\nMath.log1p = function(value) {};\\n\\n/**\\n * @param {number} value\\n * @return {number}\\n * @nosideeffects\\n */\\nMath.expm1 = function(value) {};\\n\\n/**\\n * @param {number} value\\n * @return {number}\\n * @nosideeffects\\n */\\nMath.cosh = function(value) {};\\n\\n/**\\n * @param {number} value\\n * @return {number}\\n * @nosideeffects\\n */\\nMath.sinh = function(value) {};\\n\\n/**\\n * @param {number} value\\n * @return {number}\\n * @nosideeffects\\n */\\nMath.tanh = function(value) {};\\n\\n/**\\n * @param {number} value\\n * @return {number}\\n * @nosideeffects\\n */\\nMath.acosh = function(value) {};\\n\\n/**\\n * @param {number} value\\n * @return {number}\\n * @nosideeffects\\n */\\nMath.asinh = function(value) {};\\n\\n/**\\n * @param {number} value\\n * @return {number}\\n * @nosideeffects\\n */\\nMath.atanh = function(value) {};\\n\\n/**\\n * @param {number} value\\n * @return {number}\\n * @nosideeffects\\n */\\nMath.trunc = function(value) {};\\n\\n/**\\n * @param {number} value\\n * @return {number}\\n * @nosideeffects\\n */\\nMath.sign = function(value) {};\\n\\n/**\\n * @param {number} value\\n * @return {number}\\n * @nosideeffects\\n */\\nMath.cbrt = function(value) {};\\n\\n/**\\n * @param {...number} var_args\\n * @return {number}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/hypot\\n */\\nMath.hypot = function(var_args) {};\\n\\n/**\\n * @param {number} value1\\n * @param {number} value2\\n * @return {number}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul\\n */\\nMath.imul = function(value1, value2) {};\\n\\n/**\\n * @param {number} value\\n * @return {number}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32\\n */\\nMath.clz32 = function(value) {};\\n\\n/**\\n * @param {number} value\\n * @return {number}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround\\n */\\nMath.fround = function(value) {};\\n\\n\\n/**\\n * @param {*} a\\n * @param {*} b\\n * @return {boolean}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is\\n */\\nObject.is;\\n\\n\\n/**\\n * Returns a language-sensitive string representation of this number.\\n * @param {(string|!Array)=} opt_locales\\n * @param {Object=} opt_options\\n * @return {string}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString\\n * @see http://www.ecma-international.org/ecma-402/1.0/#sec-13.2.1\\n * @override\\n */\\nNumber.prototype.toLocaleString = function(opt_locales, opt_options) {};\\n\\n/**\\n * Returns the wrapped primitive value of this Number object.\\n * @return {number}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/valueOf\\n * @override\\n */\\nNumber.prototype.valueOf = function() {};\\n\\n\\n/**\\n * Pads the end of the string so that it reaches the given length.\\n * NOTE: this is an ES2017 (ES8) extern.\\n *\\n * @param {number} targetLength The target length.\\n * @param {string=} opt_padString The string to pad with.\\n * @this {String|string}\\n * @return {string}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd\\n */\\nString.prototype.padEnd = function(targetLength, opt_padString) {};\\n\\n/**\\n * Pads the start of the string so that it reaches the given length.\\n * NOTE: this is an ES2017 (ES8) extern.\\n *\\n * @param {number} targetLength The target length.'; +a.a+="\\n * @param {string=} opt_padString The string to pad with.\\n * @this {String|string}\\n * @return {string}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart\\n */\\nString.prototype.padStart = function(targetLength, opt_padString) {};\\n\\n/**\\n * Repeats the string the given number of times.\\n *\\n * @param {number} count The number of times the string is repeated.\\n * @this {String|string}\\n * @return {string}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat\\n */\\nString.prototype.repeat = function(count) {};\\n\\n/**\\n * @constructor\\n * @extends {Array}\\n * @see http://www.ecma-international.org/ecma-262/6.0/#sec-gettemplateobject\\n */\\nvar ITemplateArray = function() {};\\n\\n/**\\n * @type {!Array}\\n */\\nITemplateArray.prototype.raw;\\n\\n/**\\n * @param {!ITemplateArray} template\\n * @param {...*} var_args Substitution values.\\n * @return {string}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/raw\\n */\\nString.raw = function(template, var_args) {};\\n\\n\\n/**\\n * @param {number} codePoint\\n * @param {...number} var_args Additional codepoints\\n * @return {string}\\n */\\nString.fromCodePoint = function(codePoint, var_args) {};\\n\\n\\n/**\\n * @param {number} index\\n * @return {number}\\n * @nosideeffects\\n */\\nString.prototype.codePointAt = function(index) {};\\n\\n\\n/**\\n * @param {string=} opt_form\\n * @return {string}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize\\n */\\nString.prototype.normalize = function(opt_form) {};\\n\\n\\n/**\\n * @param {string} searchString\\n * @param {number=} opt_position\\n * @return {boolean}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith\\n */\\nString.prototype.startsWith = function(searchString, opt_position) {};\\n\\n/**\\n * @param {string} searchString\\n * @param {number=} opt_position\\n * @return {boolean}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith\\n */\\nString.prototype.endsWith = function(searchString, opt_position) {};\\n\\n/**\\n * @param {string} searchString\\n * @param {number=} opt_position\\n * @return {boolean}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes\\n */\\nString.prototype.includes = function(searchString, opt_position) {};\\n\\n\\n/**\\n * @see http://dev.w3.org/html5/postmsg/\\n * @interface\\n */\\nfunction Transferable() {}\\n\\n/**\\n * @param {number} length The length in bytes\\n * @constructor\\n * @throws {Error}\\n * @implements {Transferable}\\n */\\nfunction ArrayBuffer(length) {}\\n\\n/** @type {number} */\\nArrayBuffer.prototype.byteLength;\\n\\n/**\\n * @param {number} begin\\n * @param {number=} opt_end\\n * @return {!ArrayBuffer}\\n * @nosideeffects\\n */\\nArrayBuffer.prototype.slice = function(begin, opt_end) {};\\n\\n/**\\n * @param {*} arg\\n * @return {boolean}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer/isView\\n */\\nArrayBuffer.isView = function(arg) {};\\n\\n\\n/**\\n * @constructor\\n */\\nfunction ArrayBufferView() {}\\n\\n/** @type {!ArrayBuffer} */\\nArrayBufferView.prototype.buffer;\\n\\n/** @type {number} */\\nArrayBufferView.prototype.byteOffset;\\n\\n/** @type {number} */\\nArrayBufferView.prototype.byteLength;\\n\\n\\n/**\\n * @param {number} length The length in bytes\\n * @constructor\\n * @throws {Error}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer\\n */\\nfunction SharedArrayBuffer(length) {}\\n\\n/** @type {number} */\\nSharedArrayBuffer.prototype.byteLength;\\n\\n/**\\n * @param {number} begin\\n * @param {number=} opt_end\\n * @return {!SharedArrayBuffer}\\n * @nosideeffects\\n */\\nSharedArrayBuffer.prototype.slice = function(begin, opt_end) {};\\n\\n\\n/**\\n * @typedef {!ArrayBuffer|!ArrayBufferView}\\n */\\nvar BufferSource;\\n\\n\\n/**\\n * @constructor\\n * @implements {IArrayLike}\\n * @implements {Iterable}\\n * @extends {ArrayBufferView}\\n */\\nfunction TypedArray() {};\\n\\n/** @const {number} */\\nTypedArray.prototype.BYTES_PER_ELEMENT;\\n\\n/**\\n * @param {number} target\\n * @param {number} start\\n * @param {number=} opt_end\\n * @return {THIS}\\n * @this {THIS}\\n * @template THIS\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/copyWithin\\n */\\nTypedArray.prototype.copyWithin = function(target, start, opt_end) {};\\n\\n/**\\n * @return {!IteratorIterable>}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/entries\\n */\\nTypedArray.prototype.entries = function() {};\\n\\n/**\\n * @param {function(this:S, number, number, !TypedArray) : ?} callback\\n * @param {S=} opt_thisArg\\n * @return {boolean}\\n * @template S\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/every\\n */\\nTypedArray.prototype.every = function(callback, opt_thisArg) {};\\n\\n/**\\n * @param {number} value\\n * @param {number=} opt_begin\\n * @param {number=} opt_end\\n * @return {THIS}\\n * @this {THIS}\\n * @template THIS\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/fill\\n */\\nTypedArray.prototype.fill = function(value, opt_begin, opt_end) {};\\n\\n/**\\n * @param {function(this:S, number, number, !TypedArray) : boolean} callback\\n * @param {S=} opt_thisArg\\n * @return {THIS}\\n * @this {THIS}\\n * @template THIS,S\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/filter\\n */\\nTypedArray.prototype.filter = function(callback, opt_thisArg) {};\\n\\n/**\\n * @param {function(this:S, number, number, !TypedArray) : boolean} callback\\n * @param {S=} opt_thisArg\\n * @return {(number|undefined)}\\n * @template S\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/find\\n */\\nTypedArray.prototype.find = function(callback, opt_thisArg) {};\\n\\n/**\\n * @param {function(this:S, number, number, !TypedArray) : boolean} callback\\n * @param {S=} opt_thisArg\\n * @return {number}\\n * @template S\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/findIndex\\n */\\nTypedArray.prototype.findIndex = function(callback, opt_thisArg) {};\\n\\n/**\\n * @param {function(this:S, number, number, !TypedArray) : ?} callback\\n * @param {S=} opt_thisArg\\n * @return {undefined}\\n * @template S\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/forEach\\n */\\nTypedArray.prototype.forEach = function(callback, opt_thisArg) {};\\n\\n/**\\n * NOTE: this is an ES2016 (ES7) extern.\\n * @param {number} searchElement\\n * @param {number=} opt_fromIndex\\n * @return {boolean}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/includes\\n */\\nTypedArray.prototype.includes = function(searchElement, opt_fromIndex) {};\\n\\n/**\\n * @param {number} searchElement\\n * @param {number=} opt_fromIndex\\n * @return {number}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/indexOf\\n */\\nTypedArray.prototype.indexOf = function(searchElement, opt_fromIndex) {};\\n\\n/**\\n * @param {string=} opt_separator\\n * @return {string}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/join\\n */\\nTypedArray.prototype.join = function(opt_separator) {};\\n\\n/**\\n * @return {!IteratorIterable}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/keys\\n */\\nTypedArray.prototype.keys = function() {};\\n\\n/**\\n * @param {number} searchElement\\n * @param {number=} opt_fromIndex\\n * @return {number}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/lastIndexOf\\n */\\nTypedArray.prototype.lastIndexOf = function(searchElement, opt_fromIndex) {};\\n\\n/** @type {number} */\\nTypedArray.prototype.length;\\n\\n/**\\n * @param {function(this:S, number, number, !TypedArray) : number} callback\\n * @param {S=} opt_thisArg\\n * @return {THIS}\\n * @this {THIS}\\n * @template THIS,S\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/map\\n */\\nTypedArray.prototype.map = function(callback, opt_thisArg) {};\\n\\n/**\\n * @param {function((number|INIT|RET), number, number, !TypedArray) : RET} callback\\n * @param {INIT=} opt_initialValue\\n * @return {RET}\\n * @template INIT,RET\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/reduce\\n */\\nTypedArray.prototype.reduce = function(callback, opt_initialValue) {};\\n\\n/**\\n * @param {function((number|INIT|RET), number, number, !TypedArray) : RET} callback\\n * @param {INIT=} opt_initialValue\\n * @return {RET}\\n * @template INIT,RET\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/reduceRight\\n */\\nTypedArray.prototype.reduceRight = function(callback, opt_initialValue) {};\\n\\n/**\\n * @return {THIS}\\n * @this {THIS}\\n * @template THIS\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/reverse\\n */\\nTypedArray.prototype.reverse = function() {};\\n\\n/**\\n * @param {!ArrayBufferView|!Array} array\\n * @param {number=} opt_offset\\n * @return {undefined}\\n * @throws {!RangeError}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/set\\n */\\nTypedArray.prototype.set = function(array, opt_offset) {};\\n\\n/**\\n * @param {number=} opt_begin\\n * @param {number=} opt_end\\n * @return {THIS}\\n * @this {THIS}\\n * @template THIS\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/slice\\n */\\nTypedArray.prototype.slice = function(opt_begin, opt_end) {};\\n\\n/**\\n * @param {function(this:S, number, number, !TypedArray) : boolean} callback\\n * @param {S=} opt_thisArg\\n * @return {boolean}\\n * @template S\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/some\\n */\\nTypedArray.prototype.some = function(callback, opt_thisArg) {};\\n\\n/**\\n * @param {(function(number, number) : number)=} opt_compareFunction\\n * @return {THIS}\\n * @this {THIS}\\n * @template THIS\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/sort\\n */\\nTypedArray.prototype.sort = function(opt_compareFunction) {};\\n\\n/**\\n * @param {number} begin\\n * @param {number=} opt_end\\n * @return {THIS}\\n * @this {THIS}\\n * @template THIS\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/subarray\\n */\\nTypedArray.prototype.subarray = function(begin, opt_end) {};\\n\\n/**\\n * @return {!IteratorIterable}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/values\\n */\\nTypedArray.prototype.values = function() {};\\n\\n/**\\n * @return {string}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/toLocaleString\\n * @override\\n */\\nTypedArray.prototype.toLocaleString = function() {};\\n\\n/**\\n * @return {string}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/toString\\n * @override\\n */\\nTypedArray.prototype.toString = function() {};\\n\\n/** @override */\\nTypedArray.prototype[Symbol.iterator] = function() {};\\n\\n/**\\n * @param {number|ArrayBufferView|Array|ArrayBuffer|SharedArrayBuffer}\\n * length or array or buffer\\n * @param {number=} opt_byteOffset\\n * @param {number=} opt_length\\n * @constructor\\n * @extends {TypedArray}\\n * @throws {Error}\\n * @modifies {arguments} If the user passes a backing array, then indexed\\n * accesses will modify the backing array. JSCompiler does not model\\n * this well. In other words, if you have:\\n * \\n * var x = new ArrayBuffer(1);\\n * var y = new Int8Array(x);\\n * y[0] = 2;\\n * \\n * JSCompiler will not recognize that the last assignment modifies x.\\n * We workaround this by marking all these arrays as @modifies {arguments},\\n * to introduce the possibility that x aliases y.\\n */\\nfunction Int8Array(length, opt_byteOffset, opt_length) {}\\n\\n/** @const {number} */\\nInt8Array.BYTES_PER_ELEMENT;\\n\\n/**\\n * @param {string|!IArrayLike|!Iterable} source\\n * @param {function(this:S, number): number=} opt_mapFn\\n * @param {S=} opt_this\\n * @template S\\n * @return {!Int8Array}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/from\\n */\\nInt8Array.from = function(source, opt_mapFn, opt_this) {};\\n\\n/**\\n * @param {...number} var_args\\n * @return {!Int8Array}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/of\\n */\\nInt8Array.of = function(var_args) {};\\n\\n\\n/**\\n * @param {number|ArrayBufferView|Array|ArrayBuffer|SharedArrayBuffer}\\n * length or array or buffer\\n * @param {number=} opt_byteOffset\\n * @param {number=} opt_length\\n * @constructor\\n * @extends {TypedArray}\\n * @throws {Error}\\n * @modifies {arguments}\\n */\\nfunction Uint8Array(length, opt_byteOffset, opt_length) {}\\n\\n/** @const {number} */\\nUint8Array.BYTES_PER_ELEMENT;\\n\\n/**\\n * @param {string|!IArrayLike|!Iterable} source\\n * @param {function(this:S, number): number=} opt_mapFn\\n * @param {S=} opt_this\\n * @template S\\n * @return {!Uint8Array}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/from\\n */\\nUint8Array.from = function(source, opt_mapFn, opt_this) {};\\n\\n/**\\n * @param {...number} var_args\\n * @return {!Uint8Array}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/of\\n */\\nUint8Array.of = function(var_args) {};\\n\\n\\n/**\\n * @param {number|ArrayBufferView|Array|ArrayBuffer|SharedArrayBuffer}\\n * length or array or buffer\\n * @param {number=} opt_byteOffset\\n * @param {number=} opt_length\\n * @constructor\\n * @extends {TypedArray}\\n * @throws {Error}\\n * @modifies {arguments}\\n */\\nfunction Uint8ClampedArray(length, opt_byteOffset, opt_length) {}\\n\\n/** @const {number} */\\nUint8ClampedArray.BYTES_PER_ELEMENT;\\n\\n/**\\n * @param {string|!IArrayLike|!Iterable} source\\n * @param {function(this:S, number): number=} opt_mapFn\\n * @param {S=} opt_this\\n * @template S\\n * @return {!Uint8ClampedArray}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/from\\n */\\nUint8ClampedArray.from = function(source, opt_mapFn, opt_this) {};\\n\\n/**\\n * @param {...number} var_args\\n * @return {!Uint8ClampedArray}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/of\\n */\\nUint8ClampedArray.of = function(var_args) {};\\n\\n\\n/**\\n * @typedef {Uint8ClampedArray}\\n * @deprecated CanvasPixelArray has been replaced by Uint8ClampedArray\\n * in the latest spec.\\n * @see http://www.w3.org/TR/2dcontext/#imagedata\\n */\\nvar CanvasPixelArray;\\n\\n\\n/**\\n * @param {number|ArrayBufferView|Array|ArrayBuffer|SharedArrayBuffer}\\n * length or array or buffer\\n * @param {number=} opt_byteOffset\\n * @param {number=} opt_length\\n * @constructor\\n * @extends {TypedArray}\\n * @throws {Error}\\n * @modifies {arguments}\\n */\\nfunction Int16Array(length, opt_byteOffset, opt_length) {}\\n\\n/** @const {number} */\\nInt16Array.BYTES_PER_ELEMENT;\\n\\n/**\\n * @param {string|!IArrayLike|!Iterable} source\\n * @param {function(this:S, number): number=} opt_mapFn\\n * @param {S=} opt_this\\n * @template S\\n * @return"; +a.a+=" {!Int16Array}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/from\\n */\\nInt16Array.from = function(source, opt_mapFn, opt_this) {};\\n\\n/**\\n * @param {...number} var_args\\n * @return {!Int16Array}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/of\\n */\\nInt16Array.of = function(var_args) {};\\n\\n\\n/**\\n * @param {number|ArrayBufferView|Array|ArrayBuffer|SharedArrayBuffer}\\n * length or array or buffer\\n * @param {number=} opt_byteOffset\\n * @param {number=} opt_length\\n * @constructor\\n * @extends {TypedArray}\\n * @throws {Error}\\n * @modifies {arguments}\\n */\\nfunction Uint16Array(length, opt_byteOffset, opt_length) {}\\n\\n/** @const {number} */\\nUint16Array.BYTES_PER_ELEMENT;\\n\\n/**\\n * @param {string|!IArrayLike|!Iterable} source\\n * @param {function(this:S, number): number=} opt_mapFn\\n * @param {S=} opt_this\\n * @template S\\n * @return {!Uint16Array}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/from\\n */\\nUint16Array.from = function(source, opt_mapFn, opt_this) {};\\n\\n/**\\n * @param {...number} var_args\\n * @return {!Uint16Array}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/of\\n */\\nUint16Array.of = function(var_args) {};\\n\\n\\n/**\\n * @param {number|ArrayBufferView|Array|ArrayBuffer|SharedArrayBuffer}\\n * length or array or buffer\\n * @param {number=} opt_byteOffset\\n * @param {number=} opt_length\\n * @constructor\\n * @extends {TypedArray}\\n * @throws {Error}\\n * @modifies {arguments}\\n */\\nfunction Int32Array(length, opt_byteOffset, opt_length) {}\\n\\n/** @const {number} */\\nInt32Array.BYTES_PER_ELEMENT;\\n\\n/**\\n * @param {string|!IArrayLike|!Iterable} source\\n * @param {function(this:S, number): number=} opt_mapFn\\n * @param {S=} opt_this\\n * @template S\\n * @return {!Int32Array}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/from\\n */\\nInt32Array.from = function(source, opt_mapFn, opt_this) {};\\n\\n/**\\n * @param {...number} var_args\\n * @return {!Int32Array}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/of\\n */\\nInt32Array.of = function(var_args) {};\\n\\n\\n/**\\n * @param {number|ArrayBufferView|Array|ArrayBuffer|SharedArrayBuffer}\\n * length or array or buffer\\n * @param {number=} opt_byteOffset\\n * @param {number=} opt_length\\n * @constructor\\n * @extends {TypedArray}\\n * @throws {Error}\\n * @modifies {arguments}\\n */\\nfunction Uint32Array(length, opt_byteOffset, opt_length) {}\\n\\n/** @const {number} */\\nUint32Array.BYTES_PER_ELEMENT;\\n\\n/**\\n * @param {string|!IArrayLike|!Iterable} source\\n * @param {function(this:S, number): number=} opt_mapFn\\n * @param {S=} opt_this\\n * @template S\\n * @return {!Uint32Array}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/from\\n */\\nUint32Array.from = function(source, opt_mapFn, opt_this) {};\\n\\n/**\\n * @param {...number} var_args\\n * @return {!Uint32Array}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/of\\n */\\nUint32Array.of = function(var_args) {};\\n\\n\\n/**\\n * @param {number|ArrayBufferView|Array|ArrayBuffer|SharedArrayBuffer}\\n * length or array or buffer\\n * @param {number=} opt_byteOffset\\n * @param {number=} opt_length\\n * @constructor\\n * @extends {TypedArray}\\n * @throws {Error}\\n * @modifies {arguments}\\n */\\nfunction Float32Array(length, opt_byteOffset, opt_length) {}\\n\\n/** @const {number} */\\nFloat32Array.BYTES_PER_ELEMENT;\\n\\n/**\\n * @param {string|!IArrayLike|!Iterable} source\\n * @param {function(this:S, number): number=} opt_mapFn\\n * @param {S=} opt_this\\n * @template S\\n * @return {!Float32Array}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/from\\n */\\nFloat32Array.from = function(source, opt_mapFn, opt_this) {};\\n\\n/**\\n * @param {...number} var_args\\n * @return {!Float32Array}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/of\\n */\\nFloat32Array.of = function(var_args) {};\\n\\n\\n/**\\n * @param {number|ArrayBufferView|Array|ArrayBuffer|SharedArrayBuffer}\\n * length or array or buffer\\n * @param {number=} opt_byteOffset\\n * @param {number=} opt_length\\n * @constructor\\n * @extends {TypedArray}\\n * @throws {Error}\\n * @modifies {arguments}\\n */\\nfunction Float64Array(length, opt_byteOffset, opt_length) {}\\n\\n/** @const {number} */\\nFloat64Array.BYTES_PER_ELEMENT;\\n\\n/**\\n * @param {string|!IArrayLike|!Iterable} source\\n * @param {function(this:S, number): number=} opt_mapFn\\n * @param {S=} opt_this\\n * @template S\\n * @return {!Float64Array}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/from\\n */\\nFloat64Array.from = function(source, opt_mapFn, opt_this) {};\\n\\n/**\\n * @param {...number} var_args\\n * @return {!Float64Array}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/of\\n */\\nFloat64Array.of = function(var_args) {};\\n\\n\\n/**\\n * @param {ArrayBuffer|SharedArrayBuffer} buffer\\n * @param {number=} opt_byteOffset\\n * @param {number=} opt_byteLength\\n * @constructor\\n * @extends {ArrayBufferView}\\n * @throws {Error}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays/DataView\\n */\\nfunction DataView(buffer, opt_byteOffset, opt_byteLength) {}\\n\\n/**\\n * @param {number} byteOffset\\n * @return {number}\\n * @throws {Error}\\n */\\nDataView.prototype.getInt8 = function(byteOffset) {};\\n\\n/**\\n * @param {number} byteOffset\\n * @return {number}\\n * @throws {Error}\\n */\\nDataView.prototype.getUint8 = function(byteOffset) {};\\n\\n/**\\n * @param {number} byteOffset\\n * @param {boolean=} opt_littleEndian\\n * @return {number}\\n * @throws {Error}\\n */\\nDataView.prototype.getInt16 = function(byteOffset, opt_littleEndian) {};\\n\\n/**\\n * @param {number} byteOffset\\n * @param {boolean=} opt_littleEndian\\n * @return {number}\\n * @throws {Error}\\n */\\nDataView.prototype.getUint16 = function(byteOffset, opt_littleEndian) {};\\n\\n/**\\n * @param {number} byteOffset\\n * @param {boolean=} opt_littleEndian\\n * @return {number}\\n * @throws {Error}\\n */\\nDataView.prototype.getInt32 = function(byteOffset, opt_littleEndian) {};\\n\\n/**\\n * @param {number} byteOffset\\n * @param {boolean=} opt_littleEndian\\n * @return {number}\\n * @throws {Error}\\n */\\nDataView.prototype.getUint32 = function(byteOffset, opt_littleEndian) {};\\n\\n/**\\n * @param {number} byteOffset\\n * @param {boolean=} opt_littleEndian\\n * @return {number}\\n * @throws {Error}\\n */\\nDataView.prototype.getFloat32 = function(byteOffset, opt_littleEndian) {};\\n\\n/**\\n * @param {number} byteOffset\\n * @param {boolean=} opt_littleEndian\\n * @return {number}\\n * @throws {Error}\\n */\\nDataView.prototype.getFloat64 = function(byteOffset, opt_littleEndian) {};\\n\\n/**\\n * @param {number} byteOffset\\n * @param {number} value\\n * @throws {Error}\\n * @return {undefined}\\n */\\nDataView.prototype.setInt8 = function(byteOffset, value) {};\\n\\n/**\\n * @param {number} byteOffset\\n * @param {number} value\\n * @throws {Error}\\n * @return {undefined}\\n */\\nDataView.prototype.setUint8 = function(byteOffset, value) {};\\n\\n/**\\n * @param {number} byteOffset\\n * @param {number} value\\n * @param {boolean=} opt_littleEndian\\n * @throws {Error}\\n * @return {undefined}\\n */\\nDataView.prototype.setInt16 = function(byteOffset, value, opt_littleEndian) {};\\n\\n/**\\n * @param {number} byteOffset\\n * @param {number} value\\n * @param {boolean=} opt_littleEndian\\n * @throws {Error}\\n * @return {undefined}\\n */\\nDataView.prototype.setUint16 = function(byteOffset, value, opt_littleEndian) {};\\n\\n/**\\n * @param {number} byteOffset\\n * @param {number} value\\n * @param {boolean=} opt_littleEndian\\n * @throws {Error}\\n * @return {undefined}\\n */\\nDataView.prototype.setInt32 = function(byteOffset, value, opt_littleEndian) {};\\n\\n/**\\n * @param {number} byteOffset\\n * @param {number} value\\n * @param {boolean=} opt_littleEndian\\n * @throws {Error}\\n * @return {undefined}\\n */\\nDataView.prototype.setUint32 = function(byteOffset, value, opt_littleEndian) {};\\n\\n/**\\n * @param {number} byteOffset\\n * @param {number} value\\n * @param {boolean=} opt_littleEndian\\n * @throws {Error}\\n * @return {undefined}\\n */\\nDataView.prototype.setFloat32 = function(\\n byteOffset, value, opt_littleEndian) {};\\n\\n/**\\n * @param {number} byteOffset\\n * @param {number} value\\n * @param {boolean=} opt_littleEndian\\n * @throws {Error}\\n * @return {undefined}\\n */\\nDataView.prototype.setFloat64 = function(\\n byteOffset, value, opt_littleEndian) {};\\n\\n\\n/**\\n * @see https://github.com/promises-aplus/promises-spec\\n * @typedef {{then: ?}}\\n */\\nvar Thenable;\\n\\n\\n/**\\n * This is not an official DOM interface. It is used to add generic typing\\n * and respective type inference where available.\\n * {@see goog.Thenable} inherits from this making all promises\\n * interoperate.\\n * @interface\\n * @template TYPE\\n */\\nfunction IThenable() {}\\n\\n\\n/**\\n * @param {?(function(TYPE):VALUE)=} opt_onFulfilled\\n * @param {?(function(*): *)=} opt_onRejected\\n * @return {RESULT}\\n * @template VALUE\\n *\\n * When a `Thenable` is fulfilled or rejected with another `Thenable`, the\\n * payload of the second is used as the payload of the first.\\n *\\n * @template RESULT := type('IThenable',\\n * cond(isUnknown(VALUE), unknown(),\\n * mapunion(VALUE, (V) =>\\n * cond(isTemplatized(V) && sub(rawTypeOf(V), 'IThenable'),\\n * templateTypeOf(V, 0),\\n * cond(sub(V, 'Thenable'),\\n * unknown(),\\n * V)))))\\n * =:\\n */\\nIThenable.prototype.then = function(opt_onFulfilled, opt_onRejected) {};\\n\\n\\n/**\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise\\n * @param {function(\\n * function((TYPE|IThenable|Thenable|null)=),\\n * function(*=))} resolver\\n * @constructor\\n * @implements {IThenable}\\n * @template TYPE\\n */\\nfunction Promise(resolver) {}\\n\\n\\n/**\\n * @param {VALUE=} opt_value\\n * @return {RESULT}\\n * @template VALUE\\n * @template RESULT := type('Promise',\\n * cond(isUnknown(VALUE), unknown(),\\n * mapunion(VALUE, (V) =>\\n * cond(isTemplatized(V) && sub(rawTypeOf(V), 'IThenable'),\\n * templateTypeOf(V, 0),\\n * cond(sub(V, 'Thenable'),\\n * unknown(),\\n * V)))))\\n * =:\\n */\\nPromise.resolve = function(opt_value) {};\\n\\n\\n/**\\n * @param {*=} opt_error\\n * @return {!Promise}\\n */\\nPromise.reject = function(opt_error) {};\\n\\n\\n/**\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise\\n * @param {!Iterable} iterable\\n * @return {!Promise>}\\n * @template VALUE\\n * @template RESULT := mapunion(VALUE, (V) =>\\n * cond(isUnknown(V),\\n * unknown(),\\n * cond(isTemplatized(V) && sub(rawTypeOf(V), 'IThenable'),\\n * templateTypeOf(V, 0),\\n * cond(sub(V, 'Thenable'), unknown(), V))))\\n * =:\\n */\\nPromise.all = function(iterable) {};\\n\\n\\n/**\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise\\n * @param {!Iterable} iterable\\n * @return {!Promise}\\n * @template VALUE\\n * @template RESULT := mapunion(VALUE, (V) =>\\n * cond(isUnknown(V),\\n * unknown(),\\n * cond(isTemplatized(V) && sub(rawTypeOf(V), 'IThenable'),\\n * templateTypeOf(V, 0),\\n * cond(sub(V, 'Thenable'), unknown(), V))))\\n * =:\\n */\\nPromise.race = function(iterable) {};\\n\\n\\n/**\\n * @param {?(function(this:void, TYPE):VALUE)=} opt_onFulfilled\\n * @param {?(function(this:void, *): *)=} opt_onRejected\\n * @return {RESULT}\\n * @template VALUE\\n *\\n * When a `Thenable` is fulfilled or rejected with another `Thenable`, the\\n * payload of the second is used as the payload of the first.\\n *\\n * @template RESULT := type('Promise',\\n * cond(isUnknown(VALUE), unknown(),\\n * mapunion(VALUE, (V) =>\\n * cond(isTemplatized(V) && sub(rawTypeOf(V), 'IThenable'),\\n * templateTypeOf(V, 0),\\n * cond(sub(V, 'Thenable'),\\n * unknown(),\\n * V)))))\\n * =:\\n * @override\\n */\\nPromise.prototype.then = function(opt_onFulfilled, opt_onRejected) {};\\n\\n\\n/**\\n * @param {function(*):VALUE} onRejected\\n * @return {!Promise} A Promise of the original type or a possibly\\n * a different type depending on whether the parent promise was rejected.\\n *\\n * @template VALUE\\n *\\n * When a `Thenable` is rejected with another `Thenable`, the payload of the\\n * second is used as the payload of the first.\\n *\\n * @template RESULT := cond(\\n * isUnknown(VALUE),\\n * unknown(),\\n * mapunion(VALUE, (V) =>\\n * cond(\\n * isTemplatized(V) && sub(rawTypeOf(V), 'IThenable'),\\n * templateTypeOf(V, 0),\\n * cond(\\n * sub(V, 'Thenable'),\\n * unknown(),\\n * V))))\\n * =:\\n */\\nPromise.prototype.catch = function(onRejected) {};\\n\\n\\n/**\\n * @param {function()} callback\\n * @return {!Promise}\\n */\\nPromise.prototype.finally = function(callback) {};\\n\\n\\n/**\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/of\\n * @param {...T} var_args\\n * @return {!Array}\\n * @template T\\n */\\nArray.of = function(var_args) {};\\n\\n\\n/**\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from\\n * @param {string|!IArrayLike|!Iterable} arrayLike\\n * @param {function(this:S, (string|T), number): R=} opt_mapFn\\n * @param {S=} opt_this\\n * @return {!Array}\\n * @template T,S,R\\n */\\nArray.from = function(arrayLike, opt_mapFn, opt_this) {};\\n\\n\\n/** @return {!IteratorIterable} */\\nArray.prototype.keys;\\n\\n\\n/**\\n * @return {!IteratorIterable}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/values\\n */\\nArray.prototype.values;\\n\\n\\n/**\\n * @return {!IteratorIterable>} Iterator of [key, value] pairs.\\n */\\nArray.prototype.entries;\\n\\n\\n/**\\n * @param {!function(this:S, T, number, !Array): boolean} predicateFn\\n * @param {S=} opt_this\\n * @return {T|undefined}\\n * @this {IArrayLike|string}\\n * @template T,S\\n * @see http://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.find\\n */\\nArray.prototype.find = function(predicateFn, opt_this) {};\\n\\n\\n/**\\n * @param {!function(this:S, T, number, !Array): boolean} predicateFn\\n * @param {S=} opt_this\\n * @return {number}\\n * @this {IArrayLike|string}\\n * @template T,S\\n * @see http://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.findindex\\n */\\nArray.prototype.findIndex = function(predicateFn, opt_this) {};\\n\\n\\n/**\\n * @param {T} value\\n * @param {number=} opt_begin\\n * @param {number=} opt_end\\n * @return {!Array}\\n * @this {!IArrayLike|string}\\n * @template T\\n * @see http://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.fill\\n */\\nArray.prototype.fill = function(value, opt_begin, opt_end) {};\\n\\n\\n/**\\n * @param {number} target\\n * @param {number} start\\n * @param {number=} opt_end\\n * @see http://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.copywithin\\n * @template T\\n * @return {!Array}\\n */\\nArray.prototype.copyWithin = function(target, start, opt_end) {};\\n\\n\\n/**\\n * NOTE: this is an ES2016 (ES7) extern.\\n * @param {T} searchElement\\n * @param {number=} opt_fromIndex\\n * @return {boolean}\\n * @this {!IArrayLike|string}\\n * @template T\\n * @nosideeffects\\n * @see https://tc39.github.io/ecma262/#sec-array.prototype.includes\\n */\\nArray.prototype.includes = function(searchElement, opt_fromIndex) {};\\n\\n/**\\n * NOTE: this is a stage 3 proposal extern.\\n * @param {function(this: THIS, T, number, !IArrayLike): S|!Array}\\n * callback\\n * @param {THIS=} thisArg\\n * @return {!Array}"; +a.a+='\\n * @this {!IArrayLike}\\n * @template T, THIS, S\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap\\n */\\nArray.prototype.flatMap = function(callback, thisArg) {};\\n\\n/**\\n * NOTE: this is a stage 3 proposal extern.\\n * @param {*=} depth\\n * @return {!Array}\\n * @this {!IArrayLike}\\n * @template T, S\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat\\n */\\nArray.prototype.flat = function(depth) {};\\n\\n/**\\n * @param {!Object} obj\\n * @return {!Array}\\n * @see http://www.ecma-international.org/ecma-262/6.0/#sec-object.getownpropertysymbols\\n */\\nObject.getOwnPropertySymbols = function(obj) {};\\n\\n\\n/**\\n * @param {!Object} obj\\n * @param {?} proto\\n * @return {!Object}\\n * @see http://www.ecma-international.org/ecma-262/6.0/#sec-object.setprototypeof\\n */\\nObject.setPrototypeOf = function(obj, proto) {};\\n\\n\\n/**\\n * @const {number}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON\\n */\\nNumber.EPSILON;\\n\\n/**\\n * @const {number}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_SAFE_INTEGER\\n */\\nNumber.MIN_SAFE_INTEGER;\\n\\n/**\\n * @const {number}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER\\n */\\nNumber.MAX_SAFE_INTEGER;\\n\\n\\n\\n/**\\n * Parse an integer. Use of `parseInt` without `base` is strictly\\n * banned in Google. If you really want to parse octal or hex based on the\\n * leader, then pass `undefined` as the base.\\n *\\n * @param {string} string\\n * @param {number|undefined} radix\\n * @return {number}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/parseInt\\n */\\nNumber.parseInt = function(string, radix) {};\\n\\n/**\\n * @param {string} string\\n * @return {number}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/parseFloat\\n */\\nNumber.parseFloat = function(string) {};\\n\\n/**\\n * @param {number} value\\n * @return {boolean}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN\\n */\\nNumber.isNaN = function(value) {};\\n\\n/**\\n * @param {number} value\\n * @return {boolean}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isFinite\\n */\\nNumber.isFinite = function(value) {};\\n\\n/**\\n * @param {number} value\\n * @return {boolean}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger\\n */\\nNumber.isInteger = function(value) {};\\n\\n/**\\n * @param {number} value\\n * @return {boolean}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger\\n */\\nNumber.isSafeInteger = function(value) {};\\n\\n\\n\\n/**\\n * @param {!Object} target\\n * @param {...(Object|null|undefined)} var_args\\n * @return {!Object}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign\\n */\\nObject.assign = function(target, var_args) {};\\n\\n/**\\n * TODO(dbeam): find a better place for ES2017 externs like this one.\\n * NOTE: this is an ES2017 (ES8) extern.\\n * @param {!Object} obj\\n * @return {!Array} values\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values\\n * @throws {Error}\\n * @template T\\n */\\nObject.values = function(obj) {};\\n\\n/**\\n * NOTE: this is an ES2017 (ES8) extern.\\n * @param {!Object} obj\\n * @return {!Array>} entries\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries\\n * @throws {Error}\\n * @template T\\n */\\nObject.entries = function(obj) {};\\n\\n/**\\n * NOTE: this is an ES2017 (ES8) extern.\\n * @param {!Object} obj\\n * @return {!Object} descriptors\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptors\\n * @throws {Error}\\n * @template T\\n */\\nObject.getOwnPropertyDescriptors = function(obj) {};\\n\\n\\n\\n/**\\n * @const\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect\\n */\\nvar Reflect = {};\\n\\n/**\\n * @param {function(this: THIS, ...?): RESULT} targetFn\\n * @param {THIS} thisArg\\n * @param {!Array} argList\\n * @return {RESULT}\\n * @template THIS, RESULT\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/apply\\n */\\nReflect.apply = function(targetFn, thisArg, argList) {};\\n\\n/**\\n * @param {function(new: ?, ...?)} targetConstructorFn\\n * @param {!Array} argList\\n * @param {function(new: TARGET, ...?)=} opt_newTargetConstructorFn\\n * @return {TARGET}\\n * @template TARGET\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/construct\\n */\\nReflect.construct = function(targetConstructorFn, argList, opt_newTargetConstructorFn) {};\\n\\n/**\\n * @param {!Object} target\\n * @param {string} propertyKey\\n * @param {!ObjectPropertyDescriptor} attributes\\n * @return {boolean}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/defineProperty\\n */\\nReflect.defineProperty = function(target, propertyKey, attributes) {};\\n\\n/**\\n * @param {!Object} target\\n * @param {string} propertyKey\\n * @return {boolean}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/deleteProperty\\n */\\nReflect.deleteProperty = function(target, propertyKey) {};\\n\\n/**\\n * @param {!Object} target\\n * @param {string} propertyKey\\n * @param {!Object=} opt_receiver\\n * @return {*}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/get\\n */\\nReflect.get = function(target, propertyKey, opt_receiver) {};\\n\\n/**\\n * @param {!Object} target\\n * @param {string} propertyKey\\n * @return {?ObjectPropertyDescriptor}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/getOwnPropertyDescriptor\\n */\\nReflect.getOwnPropertyDescriptor = function(target, propertyKey) {};\\n\\n/**\\n * @param {!Object} target\\n * @return {?Object}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/getPrototypeOf\\n */\\nReflect.getPrototypeOf = function(target) {};\\n\\n/**\\n * @param {!Object} target\\n * @param {string} propertyKey\\n * @return {boolean}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/has\\n */\\nReflect.has = function(target, propertyKey) {};\\n\\n/**\\n * @param {!Object} target\\n * @return {boolean}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/isExtensible\\n */\\nReflect.isExtensible = function(target) {};\\n\\n/**\\n * @param {!Object} target\\n * @return {!Array<(string|symbol)>}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/ownKeys\\n */\\nReflect.ownKeys = function(target) {};\\n\\n/**\\n * @param {!Object} target\\n * @return {boolean}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/preventExtensions\\n */\\nReflect.preventExtensions = function(target) {};\\n\\n/**\\n * @param {!Object} target\\n * @param {string} propertyKey\\n * @param {*} value\\n * @param {!Object=} opt_receiver\\n * @return {boolean}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/set\\n */\\nReflect.set = function(target, propertyKey, value, opt_receiver) {};\\n\\n/**\\n * @param {!Object} target\\n * @param {?Object} proto\\n * @return {boolean}\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/setPrototypeOf\\n */\\nReflect.setPrototypeOf = function(target, proto) {};\\n\\n\\n/**\\n * @const\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics\\n */\\nvar Atomics = {};\\n\\n/**\\n * @param {!TypedArray} typedArray\\n * @param {number} index\\n * @param {number} value\\n * @return {number}\\n */\\nAtomics.add = function(typedArray, index, value) {}\\n\\n/**\\n * @param {!TypedArray} typedArray\\n * @param {number} index\\n * @param {number} value\\n * @return {number}\\n */\\nAtomics.and = function(typedArray, index, value) {}\\n\\n/**\\n * @param {!TypedArray} typedArray\\n * @param {number} index\\n * @param {number} expectedValue\\n * @param {number} replacementValue\\n * @return {number}\\n */\\nAtomics.compareExchange = function(typedArray, index, expectedValue,\\n replacementValue) {}\\n\\n/**\\n * @param {!TypedArray} typedArray\\n * @param {number} index\\n * @param {number} value\\n * @return {number}\\n */\\nAtomics.exchange = function(typedArray, index, value) {}\\n\\n/**\\n * @param {number} size\\n * @return {boolean}\\n */\\nAtomics.isLockFree = function(size) {}\\n\\n/**\\n * @param {!TypedArray} typedArray\\n * @param {number} index\\n * @return {number}\\n */\\nAtomics.load = function(typedArray, index) {}\\n\\n/**\\n * @param {!TypedArray} typedArray\\n * @param {number} index\\n * @param {number} value\\n * @return {number}\\n */\\nAtomics.or = function(typedArray, index, value) {}\\n\\n/**\\n * @param {!TypedArray} typedArray\\n * @param {number} index\\n * @param {number} value\\n * @return {number}\\n */\\nAtomics.store = function(typedArray, index, value) {}\\n\\n/**\\n * @param {!TypedArray} typedArray\\n * @param {number} index\\n * @param {number} value\\n * @return {number}\\n */\\nAtomics.sub = function(typedArray, index, value) {}\\n\\n/**\\n * @param {!Int32Array} typedArray\\n * @param {number} index\\n * @param {number} value\\n * @param {number=} timeout\\n * @return {String}\\n */\\nAtomics.wait = function(typedArray, index, value, timeout) {}\\n\\n/**\\n * @param {!Int32Array} typedArray\\n * @param {number} index\\n * @param {number} count\\n * @return {number}\\n */\\nAtomics.wake = function(typedArray, index, count) {}\\n\\n/**\\n * @param {!TypedArray} typedArray\\n * @param {number} index\\n * @param {number} value\\n * @return {number}\\n */\\nAtomics.xor = function(typedArray, index, value) {}\\n\\n\\n/**\\n * @see https://tc39.github.io/proposal-async-iteration/\\n * @const {symbol}\\n */\\nSymbol.asyncIterator;\\n\\n\\n/**\\n * @interface\\n * @template VALUE\\n * @see https://tc39.github.io/proposal-async-iteration/\\n */\\nfunction AsyncIterator() {}\\n\\n/**\\n * @param {?=} opt_value\\n * @return {!Promise>}\\n */\\nAsyncIterator.prototype.next;\\n\\n\\n/**\\n * @interface\\n * @template VALUE\\n */\\nfunction AsyncIterable() {}\\n\\n\\n/**\\n * @return {!AsyncIterator}\\n */\\nAsyncIterable.prototype[Symbol.asyncIterator] = function() {};\\n\\n\\n/**\\n * @interface\\n * @extends {AsyncIterator}\\n * @extends {AsyncIterable}\\n * @template VALUE\\n * @see https://tc39.github.io/proposal-async-iteration/\\n */\\nfunction AsyncIteratorIterable() {}\\n\\n/**\\n * @interface\\n * @see https://tc39.github.io/proposal-async-iteration/\\n * @extends {AsyncIteratorIterable}\\n * @template VALUE\\n */\\nfunction AsyncGenerator() {}\\n\\n/**\\n * @param {?=} opt_value\\n * @return {!Promise>}\\n * @override\\n */\\nAsyncGenerator.prototype.next = function(opt_value) {};\\n\\n/**\\n * @param {VALUE} value\\n * @return {!Promise>}\\n */\\nAsyncGenerator.prototype.return = function(value) {};\\n\\n/**\\n * @param {?} exception\\n * @return {!Promise>}\\n */\\nAsyncGenerator.prototype.throw = function(exception) {};\\n","externs/es6_collections.js":"/*\\n * Copyright 2014 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for ECMAScript 6.\\n * @see http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts\\n * @externs\\n */\\n\\n// TODO(johnlenz): Use Tuples for the Map and Set iterators where appropriate.\\n\\n/**\\n * @constructor @struct\\n * @param {Iterable>|!Array>=} opt_iterable\\n * @implements {Iterable>}\\n * @template KEY, VALUE\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map\\n */\\nfunction Map(opt_iterable) {}\\n\\n/** @return {void} */\\nMap.prototype.clear = function() {};\\n\\n/**\\n * @param {KEY} key\\n * @return {boolean}\\n */\\nMap.prototype.delete = function(key) {};\\n\\n/**\\n * @return {!IteratorIterable>}\\n * @nosideeffects\\n */\\nMap.prototype.entries = function() {};\\n\\n/**\\n * @param {function(this:THIS, VALUE, KEY, MAP)} callback\\n * @param {THIS=} opt_thisArg\\n * @this {MAP}\\n * @template MAP,THIS\\n */\\nMap.prototype.forEach = function(callback, opt_thisArg) {};\\n\\n/**\\n * @param {KEY} key\\n * @return {VALUE}\\n * @nosideeffects\\n */\\nMap.prototype.get = function(key) {};\\n\\n/**\\n * @param {KEY} key\\n * @return {boolean}\\n * @nosideeffects\\n */\\nMap.prototype.has = function(key) {};\\n\\n/**\\n * @return {!IteratorIterable}\\n * @nosideeffects\\n */\\nMap.prototype.keys = function() {};\\n\\n/**\\n * @param {KEY} key\\n * @param {VALUE} value\\n * @return {THIS}\\n * @this {THIS}\\n * @template THIS\\n */\\nMap.prototype.set = function(key, value) {};\\n\\n/**\\n * @type {number}\\n * (readonly)\\n */\\nMap.prototype.size;\\n\\n/**\\n * @return {!IteratorIterable}\\n * @nosideeffects\\n */\\nMap.prototype.values = function() {};\\n\\n/**\\n * @return {!Iterator>}\\n */\\nMap.prototype[Symbol.iterator] = function() {};\\n\\n\\n/**\\n * @constructor @struct\\n * @param {Iterable>|!Array>=} opt_iterable\\n * @template KEY, VALUE\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap\\n */\\nfunction WeakMap(opt_iterable) {}\\n\\n/** @return {void} */\\nWeakMap.prototype.clear = function() {};\\n\\n/**\\n * @param {KEY} key\\n * @return {boolean}\\n */\\nWeakMap.prototype.delete = function(key) {};\\n\\n/**\\n * @param {KEY} key\\n * @return {VALUE}\\n * @nosideeffects\\n */\\nWeakMap.prototype.get = function(key) {};\\n\\n/**\\n * @param {KEY} key\\n * @return {boolean}\\n * @nosideeffects\\n */\\nWeakMap.prototype.has = function(key) {};\\n\\n/**\\n * @param {KEY} key\\n * @param {VALUE} value\\n * @return {THIS}\\n * @this {THIS}\\n * @template THIS\\n */\\nWeakMap.prototype.set = function(key, value) {};\\n\\n/**\\n * @constructor @struct\\n * @param {Iterable|Array=} opt_iterable\\n * @implements {Iterable}\\n * @template VALUE\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set\\n */\\nfunction Set(opt_iterable) {}\\n\\n/**\\n * @param {VALUE} value\\n * @return {THIS}\\n * @this {THIS}\\n * @template THIS\\n */\\nSet.prototype.add = function(value) {};\\n\\n/**\\n * @return {void}\\n */\\nSet.prototype.clear = function() {};\\n\\n/**\\n * @param {VALUE} value\\n * @return {boolean}\\n */\\nSet.prototype.delete = function(value) {};\\n\\n/**\\n * @return {!IteratorIterable>} Where each array has two entries:\\n * [value, value]\\n * @nosideeffects\\n */\\nSet.prototype.entries = function() {};\\n\\n/**\\n * @param {function(this: THIS, VALUE, VALUE, SET)} callback\\n * @param {THIS=} opt_thisArg\\n * @this {SET}\\n * @template SET,THIS\\n */\\nSet.prototype.forEach = function(callback, opt_thisArg) {};\\n\\n/**\\n * @param {VALUE} value\\n * @return {boolean}\\n * @nosideeffects\\n */\\nSet.prototype.has = function(value) {};\\n\\n/**\\n * @type {number} (readonly)\\n */\\nSet.prototype.size;\\n\\n/**'; +a.a+='\\n * @return {!IteratorIterable}\\n * @nosideeffects\\n */\\nSet.prototype.keys = function() {};\\n\\n/**\\n * @return {!IteratorIterable}\\n * @nosideeffects\\n */\\nSet.prototype.values = function() {};\\n\\n/**\\n * @return {!Iterator}\\n */\\nSet.prototype[Symbol.iterator] = function() {};\\n\\n\\n\\n/**\\n * @constructor @struct\\n * @param {Iterable|Array=} opt_iterable\\n * @template VALUE\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set\\n */\\nfunction WeakSet(opt_iterable) {}\\n\\n/**\\n * @param {VALUE} value\\n * @return {THIS}\\n * @this {THIS}\\n * @template THIS\\n */\\nWeakSet.prototype.add = function(value) {};\\n\\n/**\\n * @return {void}\\n */\\nWeakSet.prototype.clear = function() {};\\n\\n/**\\n * @param {VALUE} value\\n * @return {boolean}\\n */\\nWeakSet.prototype.delete = function(value) {};\\n\\n/**\\n * @param {VALUE} value\\n * @return {boolean}\\n * @nosideeffects\\n */\\nWeakSet.prototype.has = function(value) {};\\n","externs/intl.js":"/*\\n * Copyright 2013 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for the JS Internationalization API as defined in\\n * http://www.ecma-international.org/ecma-402/1.0/\\n *\\n * @externs\\n */\\n\\n/** @const */\\nvar Intl = {};\\n\\n/**\\n * NOTE: this API is not from ecma402 and is subject to change.\\n * @param {string|Array=} opt_locales\\n * @param {{type: (string|undefined)}=}\\n * opt_options\\n * @constructor\\n */\\nIntl.v8BreakIterator = function(opt_locales, opt_options) {};\\n\\n/**\\n * @param {string} text\\n * @return {undefined}\\n */\\nIntl.v8BreakIterator.prototype.adoptText = function(text) {};\\n\\n/**\\n * @return {string}\\n */\\nIntl.v8BreakIterator.prototype.breakType = function() {};\\n\\n/**\\n * @return {number}\\n */\\nIntl.v8BreakIterator.prototype.current = function() {};\\n\\n/**\\n * @return {number}\\n */\\nIntl.v8BreakIterator.prototype.first = function() {};\\n\\n/**\\n * @return {number}\\n */\\nIntl.v8BreakIterator.prototype.next = function() {};\\n\\n/**\\n * @constructor\\n * @param {string|Array=} opt_locales\\n * @param {{usage: (string|undefined), localeMatcher: (string|undefined),\\n * sensitivity: (string|undefined), ignorePunctuation: (boolean|undefined),\\n * numeric: (boolean|undefined), caseFirst: (string|undefined)}=}\\n * opt_options\\n */\\nIntl.Collator = function(opt_locales, opt_options) {};\\n\\n/**\\n * @param {Array} locales\\n * @param {{localeMatcher: (string|undefined)}=} opt_options\\n * @return {Array}\\n */\\nIntl.Collator.supportedLocalesOf = function(locales, opt_options) {};\\n\\n/**\\n * @param {string} arg1\\n * @param {string} arg2\\n * @return {number}\\n */\\nIntl.Collator.prototype.compare = function(arg1, arg2) {};\\n\\n/**\\n * @return {{locale: string, usage: string, sensitivity: string,\\n * ignorePunctuation: boolean, collation: string, numeric: boolean,\\n * caseFirst: string}}\\n */\\nIntl.Collator.prototype.resolvedOptions = function() {};\\n\\n/**\\n * @constructor\\n * @param {string|Array=} opt_locales\\n * @param {{localeMatcher: (string|undefined), useGrouping: (boolean|undefined),\\n * numberingSystem: (string|undefined), style: (string|undefined),\\n * currency: (string|undefined), currencyDisplay: (string|undefined),\\n * minimumIntegerDigits: (number|undefined),\\n * minimumFractionDigits: (number|undefined),\\n * maximumFractionDigits: (number|undefined),\\n * minimumSignificantDigits: (number|undefined),\\n * maximumSignificantDigits: (number|undefined)}=}\\n * opt_options\\n */\\nIntl.NumberFormat = function(opt_locales, opt_options) {};\\n\\n/**\\n * @param {Array} locales\\n * @param {{localeMatcher: (string|undefined)}=} opt_options\\n * @return {Array}\\n */\\nIntl.NumberFormat.supportedLocalesOf = function(locales, opt_options) {};\\n\\n/**\\n * @param {number} num\\n * @return {string}\\n */\\nIntl.NumberFormat.prototype.format = function(num) {};\\n\\n/**\\n * @return {{locale: string, numberingSystem: string, style: string,\\n * currency: (string|undefined), currencyDisplay: (string|undefined),\\n * minimumIntegerDigits: number, minimumFractionDigits: number,\\n * maximumFractionDigits: number, minimumSignificantDigits: number,\\n * maximumSignificantDigits: number, useGrouping: boolean}}\\n */\\nIntl.NumberFormat.prototype.resolvedOptions = function() {};\\n\\n/**\\n * @constructor\\n * @param {string|Array=} opt_locales\\n * @param {{localeMatcher: (string|undefined),\\n * formatMatcher: (string|undefined), calendar: (string|undefined),\\n * numberingSystem: (string|undefined), tz: (string|undefined),\\n * weekday: (string|undefined), era: (string|undefined),\\n * year: (string|undefined), month: (string|undefined),\\n * day: (string|undefined), hour: (string|undefined),\\n * minute: (string|undefined), second: (string|undefined),\\n * timeZoneName: (string|undefined), hour12: (boolean|undefined)}=}\\n * opt_options\\n */\\nIntl.DateTimeFormat = function(opt_locales, opt_options) {};\\n\\n/**\\n * @param {Array} locales\\n * @param {{localeMatcher: string}=} opt_options\\n * @return {Array}\\n */\\nIntl.DateTimeFormat.supportedLocalesOf = function(locales, opt_options) {};\\n\\n/**\\n * @param {(!Date|number)=} date\\n * @return {string}\\n */\\nIntl.DateTimeFormat.prototype.format = function(date) {};\\n\\n/**\\n * @param {(!Date|number)=} date\\n * @return {Array<{type: string, value: string}>}\\n */\\nIntl.DateTimeFormat.prototype.formatToParts = function(date) {};\\n\\n/**\\n * @return {{locale: string, calendar: string, numberingSystem: string,\\n * timeZone: (string|undefined), weekday: (string|undefined),\\n * era: (string|undefined), year: (string|undefined),\\n * month: (string|undefined), day: (string|undefined),\\n * hour: (string|undefined), minute: (string|undefined),\\n * second: (string|undefined), timeZoneName: (string|undefined),\\n * hour12: (boolean|undefined)}}\\n */\\nIntl.DateTimeFormat.prototype.resolvedOptions = function() {};\\n","externs/w3c_event.js":"/*\\n * Copyright 2008 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for W3C\'s event specification.\\n * The whole file has been fully type annotated.\\n * Created from\\n * http://www.w3.org/TR/DOM-Level-2-Events/ecma-script-binding.html\\n *\\n * @externs\\n * @author acleung@google.com (Alan Leung)\\n */\\n\\n\\n/**\\n * @interface\\n * @see https://dom.spec.whatwg.org/#interface-eventtarget\\n */\\nfunction EventTarget() {}\\n\\n/**\\n * @param {string} type\\n * @param {EventListener|function(!Event):*} listener\\n * @param {(boolean|!AddEventListenerOptions)=} opt_options\\n * @return {undefined}\\n * @see https://dom.spec.whatwg.org/#dom-eventtarget-addeventlistener\\n */\\nEventTarget.prototype.addEventListener = function(type, listener, opt_options) {\\n};\\n\\n/**\\n * @param {string} type\\n * @param {EventListener|function(!Event):*} listener\\n * @param {(boolean|!EventListenerOptions)=} opt_options\\n * @return {undefined}\\n * @see https://dom.spec.whatwg.org/#dom-eventtarget-removeeventlistener\\n */\\nEventTarget.prototype.removeEventListener = function(\\n type, listener, opt_options) {};\\n\\n/**\\n * @param {!Event} evt\\n * @return {boolean}\\n * @see https://dom.spec.whatwg.org/#dom-eventtarget-dispatchevent\\n */\\nEventTarget.prototype.dispatchEvent = function(evt) {};\\n\\n/**\\n * @interface\\n */\\nfunction EventListener() {}\\n\\n/**\\n * @param {!Event} evt\\n * @return {undefined}\\n */\\nEventListener.prototype.handleEvent = function(evt) {};\\n\\n// The EventInit interface and the parameters to the Event constructor are part\\n// of DOM Level 3 (suggested) and the DOM \\"Living Standard\\" (mandated). They are\\n// included here as externs cannot be redefined. The same applies to other\\n// *EventInit interfaces and *Event constructors throughout this file. See:\\n// http://www.w3.org/TR/DOM-Level-3-Events/#event-initializers\\n// http://dom.spec.whatwg.org/#constructing-events\\n// https://dvcs.w3.org/hg/d4e/raw-file/tip/source_respec.htm#event-constructors\\n\\n/**\\n * @record\\n * @see https://dom.spec.whatwg.org/#dictdef-eventinit\\n */\\nfunction EventInit() {}\\n\\n/** @type {(undefined|boolean)} */\\nEventInit.prototype.bubbles;\\n\\n/** @type {(undefined|boolean)} */\\nEventInit.prototype.cancelable;\\n\\n/** @type {(undefined|boolean)} */\\nEventInit.prototype.composed;\\n\\n\\n/**\\n * @constructor\\n * @param {string} type\\n * @param {EventInit=} opt_eventInitDict\\n */\\nfunction Event(type, opt_eventInitDict) {}\\n\\n/**\\n * @const {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Events/ecma-script-binding.html\\n */\\nEvent.CAPTURING_PHASE;\\n\\n/**\\n * @const {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Events/ecma-script-binding.html\\n */\\nEvent.AT_TARGET;\\n\\n/**\\n * @const {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Events/ecma-script-binding.html\\n */\\nEvent.BUBBLING_PHASE;\\n\\n/** @type {string} */\\nEvent.prototype.type;\\n\\n/** @type {EventTarget} */\\nEvent.prototype.target;\\n\\n/** @type {EventTarget} */\\nEvent.prototype.currentTarget;\\n\\n/** @type {number} */\\nEvent.prototype.eventPhase;\\n\\n/** @type {boolean} */\\nEvent.prototype.bubbles;\\n\\n/** @type {boolean} */\\nEvent.prototype.cancelable;\\n\\n/** @type {number} */\\nEvent.prototype.timeStamp;\\n\\n/**\\n * Present for events spawned in browsers that support shadow dom.\\n * @type {Array|undefined}\\n */\\nEvent.prototype.path;\\n\\n/**\\n * Present for events spawned in browsers that support shadow dom.\\n * @type {function():Array|undefined}\\n * @see https://www.w3.org/TR/shadow-dom/#widl-Event-deepPath\\n */\\nEvent.prototype.deepPath;\\n\\n/**\\n * @return {undefined}\\n */\\nEvent.prototype.stopPropagation = function() {};\\n\\n/**\\n * @return {undefined}\\n */\\nEvent.prototype.preventDefault = function() {};\\n\\n/**\\n * @param {string} eventTypeArg\\n * @param {boolean=} canBubbleArg\\n * @param {boolean=} cancelableArg\\n * @return {undefined}\\n */\\nEvent.prototype.initEvent = function(eventTypeArg, canBubbleArg, cancelableArg) {};\\n\\n/**\\n * @record\\n * @extends {EventInit}\\n * @template T\\n * @see https://dom.spec.whatwg.org/#dictdef-customeventinit\\n */\\nfunction CustomEventInit() {}\\n\\n/** @type {(T|undefined)} */\\nCustomEventInit.prototype.detail;\\n\\n/**\\n * @constructor\\n * @extends {Event}\\n * @param {string} type\\n * @param {CustomEventInit=} opt_eventInitDict\\n * @template T\\n * @see http://www.w3.org/TR/DOM-Level-3-Events/#interface-CustomEvent\\n */\\nfunction CustomEvent(type, opt_eventInitDict) {}\\n\\n/**\\n * @param {string} eventType\\n * @param {boolean} bubbles\\n * @param {boolean} cancelable\\n * @param {T} detail\\n * @return {undefined}\\n */\\nCustomEvent.prototype.initCustomEvent = function(\\n eventType, bubbles, cancelable, detail) {};\\n\\n/**\\n * @type {T}\\n */\\nCustomEvent.prototype.detail;\\n\\n/**\\n * @interface\\n */\\nfunction DocumentEvent() {}\\n\\n/**\\n * @param {string} eventType\\n * @return {!Event}\\n */\\nDocumentEvent.prototype.createEvent = function(eventType) {};\\n\\n/**\\n * @record\\n * @extends {EventInit}\\n * @see https://w3c.github.io/uievents/#idl-uieventinit\\n */\\nfunction UIEventInit() {}\\n\\n/** @type {undefined|?Window} */\\nUIEventInit.prototype.view;\\n\\n/** @type {undefined|number} */\\nUIEventInit.prototype.detail;\\n\\n/**\\n * @constructor\\n * @extends {Event}\\n * @param {string} type\\n * @param {UIEventInit=} opt_eventInitDict\\n */\\nfunction UIEvent(type, opt_eventInitDict) {}\\n\\n/** @type {number} */\\nUIEvent.prototype.detail;\\n\\n/**\\n * @param {string} typeArg\\n * @param {boolean} canBubbleArg\\n * @param {boolean} cancelableArg\\n * @param {Window} viewArg\\n * @param {number} detailArg\\n * @return {undefined}\\n */\\nUIEvent.prototype.initUIEvent = function(typeArg, canBubbleArg, cancelableArg,\\n viewArg, detailArg) {};\\n\\n/**\\n * @record\\n * @extends {UIEventInit}\\n * @see https://w3c.github.io/uievents/#dictdef-eventmodifierinit\\n */\\nfunction EventModifierInit() {}\\n\\n/** @type {undefined|boolean} */\\nEventModifierInit.prototype.ctrlKey;\\n\\n/** @type {undefined|boolean} */\\nEventModifierInit.prototype.shiftKey;\\n\\n/** @type {undefined|boolean} */\\nEventModifierInit.prototype.altKey;\\n\\n/** @type {undefined|boolean} */\\nEventModifierInit.prototype.metaKey;\\n\\n/** @type {undefined|boolean} */\\nEventModifierInit.prototype.modifierAltGraph;\\n\\n/** @type {undefined|boolean} */\\nEventModifierInit.prototype.modifierCapsLock;\\n\\n/** @type {undefined|boolean} */\\nEventModifierInit.prototype.modifierFn;\\n\\n/** @type {undefined|boolean} */\\nEventModifierInit.prototype.modifierFnLock;\\n\\n/** @type {undefined|boolean} */\\nEventModifierInit.prototype.modifierHyper;\\n\\n/** @type {undefined|boolean} */\\nEventModifierInit.prototype.modifierNumLock;\\n\\n/** @type {undefined|boolean} */\\nEventModifierInit.prototype.modifierScrollLock;\\n\\n/** @type {undefined|boolean} */\\nEventModifierInit.prototype.modifierSuper;\\n\\n/** @type {undefined|boolean} */\\nEventModifierInit.prototype.modifierSymbol;\\n\\n/** @type {undefined|boolean} */\\nEventModifierInit.prototype.modifierSymbolLock;\\n\\n/**\\n * @record\\n * @extends {EventModifierInit}\\n * @see https://w3c.github.io/uievents/#idl-mouseeventinit\\n */\\nfunction MouseEventInit() {}\\n\\n/** @type {undefined|number} */\\nMouseEventInit.prototype.screenX;\\n\\n/** @type {undefined|number} */\\nMouseEventInit.prototype.screenY;\\n\\n/** @type {undefined|number} */\\nMouseEventInit.prototype.clientX;\\n\\n/** @type {undefined|number} */\\nMouseEventInit.prototype.clientY;\\n\\n/** @type {undefined|number} */\\nMouseEventInit.prototype.button;\\n\\n/** @type {undefined|number} */\\nMouseEventInit.prototype.buttons;\\n\\n/** @type {undefined|?EventTarget} */\\nMouseEventInit.prototype.relatedTarget;\\n\\n/**\\n * @constructor\\n * @extends {UIEvent}\\n * @param {string} type\\n * @param {MouseEventInit=} opt_eventInitDict\\n */\\nfunction MouseEvent(type, opt_eventInitDict) {}\\n\\n/** @type {number} */\\nMouseEvent.prototype.screenX;\\n\\n/** @type {number} */\\nMouseEvent.prototype.screenY;\\n\\n/** @type {number} */\\nMouseEvent.prototype.clientX;\\n\\n/** @type {number} */\\nMouseEvent.prototype.clientY;\\n\\n/** @type {boolean} */\\nMouseEvent.prototype.ctrlKey;\\n\\n/** @type {boolean} */\\nMouseEvent.prototype.shiftKey;\\n\\n/** @type {boolean} */\\nMouseEvent.prototype.altKey;\\n\\n/** @type {boolean} */\\nMouseEvent.prototype.metaKey;\\n\\n/** @type {number} */\\nMouseEvent.prototype.button;\\n\\n/** @type {EventTarget} */\\nMouseEvent.prototype.relatedTarget;\\n\\n\\n/**\\n * @constructor\\n * @extends {Event}\\n */\\nfunction MutationEvent() {}\\n\\n/** @type {Node} */\\nMutationEvent.prototype.relatedNode;\\n\\n/** @type {string} */\\nMutationEvent.prototype.prevValue;\\n\\n/** @type {string} */\\nMutationEvent.prototype.newValue;\\n\\n/** @type {string} */\\nMutationEvent.prototype.attrName;\\n\\n/** @type {number} */\\nMutationEvent.prototype.attrChange;\\n\\n/**\\n * @param {string} typeArg\\n * @param {boolean} canBubbleArg\\n * @param {boolean} cancelableArg\\n * @param {Node} relatedNodeArg\\n * @param {string} prevValueArg\\n * @param {string} newValueArg\\n * @param {string} attrNameArg\\n * @param {number} attrChangeArg\\n * @return {undefined}\\n */\\nMutationEvent.prototype.initMutationEvent = function(typeArg, canBubbleArg, cancel'; +a.a+='ableArg, relatedNodeArg, prevValueArg, newValueArg, attrNameArg, attrChangeArg) {};\\n\\n\\n// DOM3\\n/**\\n * @record\\n * @extends {EventModifierInit}\\n * @see https://w3c.github.io/uievents/#idl-keyboardeventinit\\n */\\nfunction KeyboardEventInit() {}\\n\\n/** @type {undefined|string} */\\nKeyboardEventInit.prototype.key;\\n\\n/** @type {undefined|string} */\\nKeyboardEventInit.prototype.code;\\n\\n/** @type {undefined|number} */\\nKeyboardEventInit.prototype.location;\\n\\n/** @type {undefined|boolean} */\\nKeyboardEventInit.prototype.repeat;\\n\\n/** @type {undefined|boolean} */\\nKeyboardEventInit.prototype.isComposing;\\n\\n/** @type {undefined|string} */\\nKeyboardEventInit.prototype.char;\\n\\n/** @type {undefined|string} */\\nKeyboardEventInit.prototype.locale;\\n\\n/**\\n * @constructor\\n * @extends {UIEvent}\\n * @param {string} type\\n * @param {KeyboardEventInit=} opt_eventInitDict\\n */\\nfunction KeyboardEvent(type, opt_eventInitDict) {}\\n\\n/** @type {string} */\\nKeyboardEvent.prototype.keyIdentifier;\\n\\n/** @type {boolean} */\\nKeyboardEvent.prototype.ctrlKey;\\n\\n/** @type {boolean} */\\nKeyboardEvent.prototype.shiftKey;\\n\\n/** @type {boolean} */\\nKeyboardEvent.prototype.altKey;\\n\\n/** @type {boolean} */\\nKeyboardEvent.prototype.metaKey;\\n\\n/**\\n * @param {string} keyIdentifierArg\\n * @return {boolean}\\n */\\nKeyboardEvent.prototype.getModifierState = function(keyIdentifierArg) {};\\n\\n/**\\n * @record\\n * @extends {UIEventInit}\\n * @see https://w3c.github.io/uievents/#idl-focuseventinit\\n */\\nfunction FocusEventInit() {}\\n\\n/** @type {undefined|?EventTarget} */\\nFocusEventInit.prototype.relatedTarget;\\n\\n\\n/**\\n * The FocusEvent interface provides specific contextual information associated\\n * with Focus events.\\n * http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent\\n *\\n * @constructor\\n * @extends {UIEvent}\\n * @param {string} type\\n * @param {FocusEventInit=} opt_eventInitDict\\n */\\nfunction FocusEvent(type, opt_eventInitDict) {}\\n\\n/** @type {EventTarget} */\\nFocusEvent.prototype.relatedTarget;\\n\\n\\n/**\\n * See https://dom.spec.whatwg.org/#dictdef-eventlisteneroptions\\n * @record\\n */\\nvar EventListenerOptions = function() {};\\n\\n/** @type {boolean|undefined} */\\nEventListenerOptions.prototype.capture;\\n\\n/**\\n * See https://dom.spec.whatwg.org/#dictdef-addeventlisteneroptions\\n * @record\\n * @extends {EventListenerOptions}\\n */\\nvar AddEventListenerOptions = function() {};\\n\\n/** @type {boolean|undefined} */\\nAddEventListenerOptions.prototype.passive;\\n\\n/** @type {boolean|undefined} */\\nAddEventListenerOptions.prototype.once;\\n\\n/**\\n * @record\\n * @extends {UIEventInit}\\n * @see https://w3c.github.io/uievents/#idl-inputeventinit\\n * @see https://w3c.github.io/input-events/#interface-InputEvent\\n */\\nfunction InputEventInit() {}\\n\\n/** @type {undefined|?string} */\\nInputEventInit.prototype.data;\\n\\n/** @type {undefined|boolean} */\\nInputEventInit.prototype.isComposing;\\n\\n/** @type {undefined|string} */\\nInputEventInit.prototype.inputType;\\n\\n/** @type {undefined|?DataTransfer} */\\nInputEventInit.prototype.dataTransfer;\\n\\n\\n// TODO(charleyroy): Add getTargetRanges() once a consensus has been made\\n// regarding how to structure these values. See\\n// https://github.com/w3c/input-events/issues/38.\\n/**\\n * @constructor\\n * @extends {UIEvent}\\n * @param {string} type\\n * @param {InputEventInit=} opt_eventInitDict\\n * @see https://www.w3.org/TR/uievents/#interface-inputevent\\n * @see https://w3c.github.io/input-events/#interface-InputEvent\\n */\\nfunction InputEvent(type, opt_eventInitDict) {}\\n\\n/** @type {string} */\\nInputEvent.prototype.data;\\n\\n/** @type {boolean} */\\nInputEvent.prototype.isComposed;\\n\\n/** @type {string} */\\nInputEvent.prototype.inputType;\\n\\n/** @type {?DataTransfer} */\\nInputEvent.prototype.dataTransfer;\\n","externs/w3c_event3.js":"/*\\n * Copyright 2010 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for W3C\'s event Level 3 specification.\\n * This file depends on w3c_event.js.\\n * The whole file has been partially type annotated.\\n * Created from\\n * http://www.w3.org/TR/DOM-Level-3-Events/#ecma-script-binding-ecma-binding\\n *\\n * @externs\\n * @author ericdingle@google.com (Eric Dingle)\\n */\\n\\n/**\\n * @param {string} typeArg\\n * @param {boolean} canBubbleArg\\n * @param {boolean} cancelableArg\\n * @param {Window} viewArg\\n * @param {string} keyIdentifierArg\\n * @param {number} keyLocationArg\\n * @param {string} modifiersList\\n * @return {undefined}\\n */\\nKeyboardEvent.prototype.initKeyboardEvent = function(typeArg, canBubbleArg, cancelableArg, viewArg, keyIdentifierArg, keyLocationArg, modifiersList) {};\\n\\n/** @type {string} */\\nKeyboardEvent.prototype.char;\\n\\n/** @type {string} */\\nKeyboardEvent.prototype.code;\\n\\n/** @type {string} */\\nKeyboardEvent.prototype.key;\\n\\n/** @type {number} */\\nKeyboardEvent.prototype.location;\\n\\n/** @type {boolean} */\\nKeyboardEvent.prototype.repeat;\\n\\n/** @type {string} */\\nKeyboardEvent.prototype.locale;\\n\\n/** @type {number} */\\nMouseEvent.prototype.buttons;\\n\\n/**\\n * @param {string} keyIdentifierArg\\n * @return {boolean}\\n */\\nMouseEvent.prototype.getModifierState = function(keyIdentifierArg) {};\\n\\n/** @type {boolean} */\\nEvent.prototype.defaultPrevented;\\n\\n/** @type {string} */\\nEvent.prototype.namespaceURI;\\n\\n/** @return {undefined} */\\nEvent.prototype.stopImmediatePropagation = function() {};\\n","externs/gecko_event.js":"/*\\n * Copyright 2008 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for all the extensions over\\n * W3C\'s event specification by Gecko. This file depends on\\n * w3c_event.js.\\n *\\n * @externs\\n * @author acleung@google.com (Alan Leung)\\n */\\n\\n// TODO: Almost all of it has not been annotated with types.\\n\\n/** @type {number} */ Event.prototype.HORIZONTAL_AXIS;\\n/** @type {number} */ Event.prototype.VERTICAL_AXIS;\\n/** @type {boolean} */ Event.prototype.altKey;\\n/** @type {number} */ Event.prototype.axis;\\n/** @type {number} */ Event.prototype.button;\\n/** @type {boolean} */ Event.prototype.cancelBubble;\\n/** @type {number} */ Event.prototype.charCode;\\n/** @type {number} */ Event.prototype.clientX;\\n/** @type {number} */ Event.prototype.clientY;\\n/** @type {boolean} */ Event.prototype.ctrlKey;\\n/** @type {EventTarget} */ Event.prototype.explicitOriginalTarget;\\n/** @type {boolean} */ Event.prototype.isChar;\\n/** @type {boolean} */ Event.prototype.isTrusted;\\n/** @type {number} */ Event.prototype.keyCode;\\n/** @type {number} */ Event.prototype.layerX;\\n/** @type {number} */ Event.prototype.layerY;\\n/** @type {boolean} */ Event.prototype.metaKey;\\n/** @type {EventTarget} */ Event.prototype.originalTarget;\\n/** @type {number} */ Event.prototype.pageX;\\n/** @type {number} */ Event.prototype.pageY;\\n/** @type {EventTarget|undefined} */ Event.prototype.relatedTarget;\\n/** @type {number} */ Event.prototype.screenX;\\n/** @type {number} */ Event.prototype.screenY;\\n/** @type {boolean} */ Event.prototype.shiftKey;\\n/** @type {Window} */ Event.prototype.view;\\n/** @type {number} */ Event.prototype.which;\\n\\n/** @constructor */ function nsIDOMPageTransitionEvent() {}\\n/** @type {boolean} */ nsIDOMPageTransitionEvent.prototype.persisted;\\n\\n//Methods\\nEvent.prototype.initKeyEvent;\\n/**\\n * @param {string} typeArg\\n * @param {boolean=} canBubbleArg\\n * @param {boolean=} cancelableArg\\n * @param {?Window=} viewArg\\n * @param {?number=} detailArg\\n * @param {number=} screenXArg\\n * @param {number=} screenYArg\\n * @param {number=} clientXArg\\n * @param {number=} clientYArg\\n * @param {boolean=} ctrlKeyArg\\n * @param {boolean=} altKeyArg\\n * @param {boolean=} shiftKeyArg\\n * @param {boolean=} metaKeyArg\\n * @param {?number=} buttonArg\\n * @param {?EventTarget=} relatedTargetArg\\n */\\nEvent.prototype.initMouseEvent = function(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg, screenXArg, screenYArg, clientXArg, clientYArg, ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg, buttonArg, relatedTargetArg) {};\\nEvent.prototype.initUIEvent;\\nEvent.prototype.initMessageEvent;\\nEvent.prototype.preventBubble;\\nEvent.prototype.preventCapture;\\n","externs/ie_event.js":"/*\\n * Copyright 2008 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for all the extensions over the\\n * W3C\'s event specification by IE in JScript. This file depends on\\n * w3c_event.js.\\n *\\n * @see http://msdn.microsoft.com/en-us/library/ms535863.aspx\\n * @externs\\n * @author acleung@google.com (Alan Leung)\\n */\\n\\n/** @type {string} */\\nEvent.prototype.Abstract;\\n\\n/** @type {boolean} */\\nEvent.prototype.altLeft;\\n\\n/** @type {string} */\\nEvent.prototype.Banner;\\n\\n/**\\n * A ClipboardData on IE, but a DataTransfer on WebKit.\\n * @see http://msdn.microsoft.com/en-us/library/ms535220.aspx\\n * @type {(ClipboardData|undefined)}\\n */\\nEvent.prototype.clipboardData;\\n\\n/** @type {boolean} */\\nEvent.prototype.contentOverflow;\\n\\n/** @type {boolean} */\\nEvent.prototype.ctrlLeft;\\n\\n/** @type {string} */\\nEvent.prototype.dataFld;\\n\\nEvent.prototype.domain;\\n\\n/** @type {Element} */\\nEvent.prototype.fromElement;\\n\\n/** @type {string} */\\nEvent.prototype.MoreInfo;\\n\\n/** @type {string} */\\nEvent.prototype.nextPage;\\n\\n/** @type {number} */\\nEvent.prototype.offsetX;\\n\\n/** @type {number} */\\nEvent.prototype.offsetY;\\n\\n/** @type {string} */\\nEvent.prototype.propertyName;\\n\\n/** @type {string} */\\nEvent.prototype.qualifier;\\n\\n/** @type {number} */\\nEvent.prototype.reason;\\n\\n/** @type {Object<*>} */\\nEvent.prototype.recordset;\\n\\n/** @type {boolean} */\\nEvent.prototype.repeat;\\n\\n/** @type {(boolean|string|undefined)} */\\nEvent.prototype.returnValue;\\n\\n/** @type {string} */\\nEvent.prototype.saveType;\\n\\nEvent.prototype.scheme;\\n\\n/** @type {boolean} */\\nEvent.prototype.shiftLeft;\\n\\n/** @type {Window} */\\nEvent.prototype.source;\\n\\n/** @type {Element} */\\nEvent.prototype.srcElement;\\n\\nEvent.prototype.srcFilter;\\n\\n/** @type {string} */\\nEvent.prototype.srcUrn;\\n\\n/** @type {Element} */\\nEvent.prototype.toElement;\\n\\nEvent.prototype.userName;\\n\\n/** @type {number} */\\nEvent.prototype.wheelDelta;\\n\\n/** @type {number} */\\nEvent.prototype.x;\\n\\n/** @type {number} */\\nEvent.prototype.y;\\n\\n/**\\n * @constructor\\n * @see http://msdn.microsoft.com/en-us/library/windows/apps/hh441257.aspx\\n */\\nfunction MSPointerPoint() {}\\n\\n/** @type {number} */\\nMSPointerPoint.prototype.pointerId;\\n\\n/** @type {number} */\\nMSPointerPoint.prototype.pointerType;\\n\\n/**\\n * @constructor\\n * @extends {Event}\\n * @see http://msdn.microsoft.com/en-us/library/windows/apps/hh441233.aspx\\n */\\nfunction MSPointerEvent() {}\\n\\n/** @type {number} */\\nMSPointerEvent.MSPOINTER_TYPE_MOUSE;\\n\\n/** @type {number} */\\nMSPointerEvent.MSPOINTER_TYPE_PEN;\\n\\n/** @type {number} */\\nMSPointerEvent.MSPOINTER_TYPE_TOUCH;\\n\\n/** @type {number} */\\nMSPointerEvent.prototype.height;\\n\\n/** @type {number} */\\nMSPointerEvent.prototype.hwTimestamp;\\n\\n/** @type {boolean} */\\nMSPointerEvent.prototype.isPrimary;\\n\\n/** @type {number} */\\nMSPointerEvent.prototype.pointerId;\\n\\n/** @type {number} */\\nMSPointerEvent.prototype.pointerType;\\n\\n/** @type {number} */\\nMSPointerEvent.prototype.pressure;\\n\\n/** @type {number} */\\nMSPointerEvent.prototype.rotation;\\n\\n/** @type {number} */\\nMSPointerEvent.prototype.tiltX;\\n\\n/** @type {number} */\\nMSPointerEvent.prototype.tiltY;\\n\\n/** @type {number} */\\nMSPointerEvent.prototype.timeStamp;\\n\\n/** @type {number} */\\nMSPointerEvent.prototype.width;\\n\\n/**\\n * @param {number} pointerId\\n * @return {undefined}\\n */\\nMSPointerEvent.prototype.msReleasePointerCapture;\\n\\n/**\\n * @param {number} pointerId\\n * @return {undefined}\\n */\\nMSPointerEvent.prototype.msSetPointerCapture;\\n\\n/**\\n * @param {string} typeArg\\n * @param {boolean} canBubbleArg\\n * @param {boolean} cancelableArg\\n * @param {Window} viewArg\\n * @param {number} detailArg\\n * @param {number} screenXArg\\n * @param {number} screenYArg\\n * @param {number} clientXArg\\n * @param {number} clientYArg\\n * @param {boolean} ctrlKeyArg\\n * @param {boolean} altKeyArg\\n * @param {boolean} shiftKeyArg\\n * @param {boolean} metaKeyArg\\n * @param {number} buttonArg\\n * @param {Element} relatedTargetArg\\n * @param {number} offsetXArg\\n * @param {number} offsetYArg\\n * @param {number} widthArg\\n * @param {number} heightArg\\n * @param {number} pressure\\n * @param {number} rotation\\n * @param {number} tiltX\\n * @param {number} tiltY\\n * @param {number} pointerIdArg\\n * @param {number} pointerType\\n * @param {number} hwTimestampArg\\n * @param {boolean} isPrimary\\n * @return {undefined}\\n * @see http://msdn.microsoft.com/en-us/library/windows/apps/hh441246.aspx\\n */\\nMSPointerEvent.prototype.initPointerEvent;\\n\\n/**\\n * @constructor\\n * @see http://msdn.microsoft.com/en-us/library/ie/hh968249(v=vs.85).aspx\\n */\\nfunction MSGesture() {}\\n\\n/**\\n * @type {Element}\\n */\\nMSGesture.prototype.target;\\n\\n/**\\n * @param {number} pointerId\\n * @return {undefined}\\n */\\nMSGesture.prototype.addPointer = function(pointerId) {};\\n\\nMSGesture.prototype.stop = function() {};\\n\\n/**\\n * @constructor\\n * @extends {Event}\\n * @see http://msdn.microsoft.com/en-us/library/ie/hh772076(v=vs.85).aspx\\n */\\nfunction MSGestureEvent() {}\\n\\n/** @type {number} */\\nMSGestureEvent.prototype.expansion;\\n\\n/** @type {!MSGesture} */\\nMSGestureEvent.prototype.gestureObject;\\n\\n/** @type {number} */\\nMSGestureEvent.prototype.hwTimestamp;\\n\\n/** @type {number} */\\nMSGestureEvent.prototype.rotation;\\n\\n/** @type {number} */\\nMSGestureEvent.prototype.scale;\\n\\n/** @type {number} */\\nMSGestureEvent.prototype.translationX;\\n\\n/** @type {number} */\\nMSGestureEvent.prototype.translationY;\\n\\n/** @type {number} */\\nMSGestureEvent.prototype.velocityAngular;\\n\\n/** @type {number} */\\nMSGestureEvent.prototype.velocityExpansion;\\n\\n/** @type {number} */\\nMSGestureEvent.prototype.velocityX;\\n\\n/** @type {number} */\\nMSGestureEvent.prototype.velocityY;\\n\\n/**\\n * @param {string} typeArg\\n * @param {boolean} canBubbleArg\\n * @param {boolean} cancelableArg\\n * @param {Window} viewArg\\n * @param {number} detailArg\\n * @param {number} screenXArg\\n * @param {number} screenYArg\\n * @param {number} clientXArg\\n * @param {number} clientYArg\\n * @param {number} offsetXArg\\n * @param {number} offsetYArg\\n * @param {number} translationXArg\\n * @param {number} translationYArg\\n * @param {number} scaleArg\\n * @param {number} expansionArg\\n * @param {number} rotationArg\\n * @param {number} velocityXArg\\n * @param {number} velocityYArg\\n * @param {number} velocityExpansionArg\\n * @param {number} velocityAngularArg\\n * @param {number} hwTimestampArg\\n * @param {EventTarget} relatedTargetArg\\n * @return {undefined}\\n * @see http://ms'; +a.a+='dn.microsoft.com/en-us/library/windows/apps/hh441187.aspx\\n */\\nMSGestureEvent.prototype.initGestureEvent;\\n","externs/webkit_event.js":"/*\\n * Copyright 2009 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for all the extensions over W3C\'s\\n * event specification by WebKit. This file depends on w3c_event.js.\\n * All the provided definitions have been type annotated\\n *\\n * @externs\\n * @author djlee@google.com (DJ Lee)\\n */\\n\\n/** @type {number} */\\nEvent.prototype.wheelDeltaX;\\n\\n/** @type {number} */\\nEvent.prototype.wheelDeltaY;\\n\\n/**\\n * @constructor\\n * @extends {Event}\\n * @see http://developer.apple.com/library/safari/documentation/AudioVideo/Reference/WebKitAnimationEventClassReference/WebKitAnimationEvent/WebKitAnimationEvent.html\\n */\\nfunction WebKitAnimationEvent() {}\\n\\n/**\\n * @type {string}\\n * @const\\n */\\nWebKitAnimationEvent.prototype.animationName;\\n\\n/**\\n * @type {number}\\n * @const\\n */\\nWebKitAnimationEvent.prototype.elapsedTime;","externs/w3c_device_sensor_event.js":"/*\\n * Copyright 2013 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for W3C\'s device orientation and device motion\\n * events specification.\\n * This file depends on w3c_event.js.\\n * The whole file has been partially type annotated.\\n * Created from http://dev.w3.org/geo/api/spec-source-orientation.\\n *\\n * @externs\\n * @author lukaszbk@google.com (Lukasz Bieniasz-Krzywiec)\\n */\\n\\n/**\\n * @record\\n * @extends {EventInit}\\n * @see https://w3c.github.io/deviceorientation/spec-source-orientation.html#deviceorientation\\n */\\nfunction DeviceOrientationEventInit() {}\\n\\n/** @type {number|undefined} */\\nDeviceOrientationEventInit.prototype.alpha;\\n\\n/** @type {number|undefined} */\\nDeviceOrientationEventInit.prototype.beta;\\n\\n/** @type {number|undefined} */\\nDeviceOrientationEventInit.prototype.gamma;\\n\\n/** @type {boolean|undefined} */\\nDeviceOrientationEventInit.prototype.absolute;\\n\\n/**\\n * @constructor\\n * @extends {Event}\\n * @param {string} type\\n * @param {!DeviceOrientationEventInit=} opt_eventInitDict\\n */\\nfunction DeviceOrientationEvent(type, opt_eventInitDict) {}\\n\\n/** @type {?number} */\\nDeviceOrientationEvent.prototype.alpha;\\n\\n/** @type {?number} */\\nDeviceOrientationEvent.prototype.beta;\\n\\n/** @type {?number} */\\nDeviceOrientationEvent.prototype.gamma;\\n\\n/** @type {boolean} */\\nDeviceOrientationEvent.prototype.absolute;\\n\\n/**\\n * @type {?number}\\n * @see https://developer.apple.com/library/safari/documentation/SafariDOMAdditions/Reference/DeviceOrientationEventClassRef/DeviceOrientationEvent/DeviceOrientationEvent.html#//apple_ref/javascript/instp/DeviceOrientationEvent/webkitCompassAccuracy\\n */\\nDeviceOrientationEvent.prototype.webkitCompassAccuracy;\\n\\n/**\\n * @type {?number}\\n * @see https://developer.apple.com/library/safari/documentation/SafariDOMAdditions/Reference/DeviceOrientationEventClassRef/DeviceOrientationEvent/DeviceOrientationEvent.html#//apple_ref/javascript/instp/DeviceOrientationEvent/webkitCompassHeading\\n */\\nDeviceOrientationEvent.prototype.webkitCompassHeading;\\n\\n/**\\n * @constructor\\n */\\nfunction DeviceAcceleration() {}\\n\\n/** @type {?number} */\\nDeviceAcceleration.prototype.x;\\n\\n/** @type {?number} */\\nDeviceAcceleration.prototype.y;\\n\\n/** @type {?number} */\\nDeviceAcceleration.prototype.z;\\n\\n/**\\n * @constructor\\n */\\nfunction DeviceRotationRate() {}\\n\\n/** @type {?number} */\\nDeviceRotationRate.prototype.alpha;\\n\\n/** @type {?number} */\\nDeviceRotationRate.prototype.beta;\\n\\n/** @type {?number} */\\nDeviceRotationRate.prototype.gamma;\\n\\n/**\\n * @constructor\\n * @extends {Event}\\n */\\nfunction DeviceMotionEvent() {}\\n\\n/** @type {?DeviceAcceleration} */\\nDeviceMotionEvent.prototype.acceleration;\\n\\n/** @type {?DeviceAcceleration} */\\nDeviceMotionEvent.prototype.accelerationIncludingGravity;\\n\\n/** @type {?DeviceRotationRate} */\\nDeviceMotionEvent.prototype.rotationRate;\\n\\n/** @type {?number} */\\nDeviceMotionEvent.prototype.interval;\\n","externs/web_app_manifest.js":"/*\\n * Copyright 2017 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n/**\\n * @fileoverview Externs for web app manifest APIs.\\n *\\n * @see https://www.w3.org/TR/appmanifest/\\n * @externs\\n */\\n\\n/**\\n * @see https://www.w3.org/TR/appmanifest/#beforeinstallpromptevent-interface\\n * @constructor\\n * @extends {Event}\\n */\\nfunction BeforeInstallPromptEvent() {}\\n\\n/** @type {!Promise<{outcome: !AppBannerPromptOutcome}>} */\\nBeforeInstallPromptEvent.prototype.userChoice;\\n\\n/** @return {!Promise} */\\nBeforeInstallPromptEvent.prototype.prompt = function() {};\\n\\n/**\\n * @typedef {string}\\n * @see https://www.w3.org/TR/appmanifest/#appbannerpromptoutcome-enum\\n * Possible values: \'accepted\', \'dismissed\'\\n */\\nvar AppBannerPromptOutcome;\\n\\n/** @typedef {{userChoice: !AppBannerPromptOutcome}} */\\nvar PromptResponseObject;\\n\\n/** @type {?function(!BeforeInstallPromptEvent)} */\\nWindow.prototype.onbeforeinstallprompt;\\n\\n/** @type {?function(!Event)} */\\nWindow.prototype.onappinstalled;\\n","externs/w3c_trusted_types.js":"/*\\n * Copyright 2018 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for W3C\'s Trusted Types specification.\\n * @see https://github.com/WICG/trusted-types\\n * @externs\\n */\\n\\n\\n/** @constructor */\\nfunction TrustedHTML() {}\\n\\n/** @constructor */\\nfunction TrustedScript() {}\\n\\n/** @constructor */\\nfunction TrustedScriptURL() {}\\n\\n/** @constructor */\\nfunction TrustedURL() {}\\n\\n\\n/** @constructor */\\nfunction TrustedTypePolicy() {}\\n\\n/**\\n * @param {string} s\\n * @return {!TrustedHTML}\\n */\\nTrustedTypePolicy.prototype.createHTML = function(s) {};\\n\\n/**\\n * @param {string} s\\n * @return {!TrustedScript}\\n */\\nTrustedTypePolicy.prototype.createScript = function(s) {};\\n\\n/**\\n * @param {string} s\\n * @return {!TrustedScriptURL}\\n */\\nTrustedTypePolicy.prototype.createScriptURL = function(s) {};\\n\\n/**\\n * @param {string} s\\n * @return {!TrustedURL}\\n */\\nTrustedTypePolicy.prototype.createURL = function(s) {};\\n\\n\\n/** @constructor */\\nfunction TrustedTypePolicyFactory() {}\\n\\n/**\\n * @param {string} name\\n * @param {{\\n * createHTML: function(string): string,\\n * createScript: function(string): string,\\n * createScriptURL: function(string): string,\\n * createURL: function(string): string}} policy\\n * @param {boolean=} opt_expose\\n * @return {!TrustedTypePolicy}\\n */\\nTrustedTypePolicyFactory.prototype.createPolicy = function(\\n name, policy, opt_expose) {};\\n\\n/**\\n * @param {string} name\\n * @return {!TrustedTypePolicy}\\n */\\nTrustedTypePolicyFactory.prototype.getExposedPolicy = function(name) {};\\n\\n/** @return {!Array} */\\nTrustedTypePolicyFactory.prototype.getPolicyNames = function() {};\\n\\n\\n/** @type {!TrustedTypePolicyFactory} */\\nvar TrustedTypes;\\n","externs/w3c_dom1.js":"/*\\n * Copyright 2008 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for W3C\'s DOM Level 1 specification.\\n * The whole file has been fully type annotated. Created from\\n * http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html\\n *\\n * @externs\\n * @author acleung@google.com (Alan Leung)\\n * @author stevey@google.com (Steve Yegge)\\n */\\n\\n/**\\n * @constructor\\n * @param {string=} message\\n * @param {string=} name\\n * @see https://heycam.github.io/webidl/#idl-DOMException\\n */\\nfunction DOMException(message, name) {}\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-258A00AF\\n */\\nDOMException.INDEX_SIZE_ERR = 1;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-258A00AF\\n */\\nDOMException.DOMSTRING_SIZE_ERR = 2;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-258A00AF\\n */\\nDOMException.HIERARCHY_REQUEST_ERR = 3;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-258A00AF\\n */\\nDOMException.WRONG_DOCUMENT_ERR = 4;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-258A00AF\\n */\\nDOMException.INVALID_CHARACTER_ERR = 5;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-258A00AF\\n */\\nDOMException.NO_DATA_ALLOWED_ERR = 6;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-258A00AF\\n */\\nDOMException.NO_MODIFICATION_ALLOWED_ERR = 7;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-258A00AF\\n */\\nDOMException.NOT_FOUND_ERR = 8;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-258A00AF\\n */\\nDOMException.NOT_SUPPORTED_ERR = 9;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-258A00AF\\n */\\nDOMException.INUSE_ATTRIBUTE_ERR = 10;\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-258A00AF\\n */\\nfunction ExceptionCode() {}\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-102161490\\n */\\nfunction DOMImplementation() {}\\n\\n/**\\n * @param {string} feature\\n * @param {string} version\\n * @return {boolean}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-5CED94D7\\n * @nosideeffects\\n */\\nDOMImplementation.prototype.hasFeature = function(feature, version) {};\\n\\n/**\\n * @constructor\\n * @implements {EventTarget}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247\\n */\\nfunction Node() {}\\n\\n/** @override */\\nNode.prototype.addEventListener = function(type, listener, opt_options) {};\\n\\n/** @override */\\nNode.prototype.removeEventListener = function(type, listener, opt_options) {};\\n\\n/** @override */\\nNode.prototype.dispatchEvent = function(evt) {};\\n\\n/**\\n * @type {NamedNodeMap}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-attributes\\n */\\nNode.prototype.attributes;\\n\\n/**\\n * @type {!NodeList}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-childNodes\\n */\\nNode.prototype.childNodes;\\n\\n/**\\n * @type {Node}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-firstChild\\n */\\nNode.prototype.firstChild;\\n\\n/**\\n * @type {Node}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-lastChild\\n */\\nNode.prototype.lastChild;\\n\\n/**\\n * @type {Node}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-nextSibling\\n */\\nNode.prototype.nextSibling;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-nodeName\\n */\\nNode.prototype.nodeName;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-nodeValue\\n */\\nNode.prototype.nodeValue;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-nodeType\\n */\\nNode.prototype.nodeType;\\n\\n/**\\n * @type {Document}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-ownerDocument\\n */\\nNode.prototype.ownerDocument;\\n\\n/**\\n * @type {Node}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-parentNode\\n */\\nNode.prototype.parentNode;\\n\\n/**\\n * @type {Node}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-previousSibling\\n */\\nNode.prototype.previousSibling;\\n\\n/**\\n * @param {Node} newChild\\n * @return {!Node}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-appendChild\\n */\\nNode.prototype.appendChild = function(newChild) {};\\n\\n/**\\n * @param {boolean} deep\\n * @return {!Node}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-cloneNode\\n * @nosideeffects\\n */\\nNode.prototype.cloneNode = function(deep) {};\\n\\n/**\\n * @return {boolean}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-hasChildNodes\\n * @nosideeffects\\n */\\nNode.prototype.hasChildNodes = function() {};\\n\\n/**\\n * @param {Node} newChild\\n * @param {Node} refChild\\n * @return {!Node}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-insertBefore\\n */\\nNode.prototype.insertBefore = function(newChild, refChild) {};\\n\\n/**\\n * @param {Node} oldChild\\n * @return {!Node}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-removeChild\\n */\\nNode.prototype.removeChild = function(oldChild) {};\\n\\n/**\\n * @param {Node} newChild\\n * @param {Node} oldChild\\n * @return {!Node}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-replaceChild\\n */\\nNode.prototype.replaceChild = function(newChild, oldChild) {};\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247\\n */\\nNode.ATTRIBUTE_NODE;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247\\n */\\nNode.CDATA_SECTION_NODE;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/T'; +a.a+="R/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247\\n */\\nNode.COMMENT_NODE;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247\\n */\\nNode.DOCUMENT_FRAGMENT_NODE;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247\\n */\\nNode.DOCUMENT_NODE;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247\\n */\\nNode.DOCUMENT_TYPE_NODE;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247\\n */\\nNode.ELEMENT_NODE;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247\\n */\\nNode.ENTITY_NODE;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247\\n */\\nNode.ENTITY_REFERENCE_NODE;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247\\n */\\nNode.PROCESSING_INSTRUCTION_NODE;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247\\n */\\nNode.TEXT_NODE;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247\\n */\\nNode.XPATH_NAMESPACE_NODE;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247\\n */\\nNode.NOTATION_NODE;\\n\\n/**\\n * @constructor\\n * @extends {Node}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-B63ED1A3\\n */\\nfunction DocumentFragment() {}\\n\\n/**\\n * @constructor\\n * @extends {Node}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#i-Document\\n */\\nfunction Document() {}\\n\\n/**\\n * @type {DocumentType}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-doctype\\n */\\nDocument.prototype.doctype;\\n\\n/**\\n * @type {!HTMLHtmlElement}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-documentElement\\n */\\nDocument.prototype.documentElement;\\n\\n/**\\n * @type {DOMImplementation}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-implementation\\n */\\nDocument.prototype.implementation;\\n\\n/**\\n * @param {string} name\\n * @return {!Attr}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-createAttribute\\n * @nosideeffects\\n */\\nDocument.prototype.createAttribute = function(name) {};\\n\\n/**\\n * @param {string} data\\n * @return {!Comment}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-createComment\\n * @nosideeffects\\n */\\nDocument.prototype.createComment = function(data) {};\\n\\n/**\\n * @param {string} data\\n * @return {!CDATASection}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-createCDATASection\\n * @nosideeffects\\n */\\nDocument.prototype.createCDATASection = function(data) {};\\n\\n/**\\n * @return {!DocumentFragment}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-createDocumentFragment\\n * @nosideeffects\\n */\\nDocument.prototype.createDocumentFragment = function() {};\\n\\n/**\\n * Create a DOM element.\\n *\\n * Web components introduced the second parameter as a way of extending existing\\n * tags (e.g. document.createElement('button', 'fancy-button')).\\n *\\n * @param {string} tagName\\n * @param {string=} opt_typeExtension\\n * @return {!Element}\\n * @nosideeffects\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-createElement\\n * @see http://w3c.github.io/webcomponents/spec/custom/#extensions-to-document-interface-to-instantiate\\n */\\nDocument.prototype.createElement = function(tagName, opt_typeExtension) {};\\n\\n/**\\n * @param {string} name\\n * @return {!EntityReference}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-createEntityReference\\n * @nosideeffects\\n */\\nDocument.prototype.createEntityReference = function(name) {};\\n\\n/**\\n * @param {string} target\\n * @param {string} data\\n * @return {!ProcessingInstruction}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-createProcessingInstruction\\n * @nosideeffects\\n */\\nDocument.prototype.createProcessingInstruction = function(target, data) {};\\n\\n/**\\n * @param {number|string} data\\n * @return {!Text}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-createTextNode\\n * @nosideeffects\\n */\\nDocument.prototype.createTextNode = function(data) {};\\n\\n/**\\n * @param {string} tagname\\n * @return {!NodeList}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-A6C9094\\n * @nosideeffects\\n */\\nDocument.prototype.getElementsByTagName = function(tagname) {};\\n\\n/**\\n * @constructor\\n * @implements {IArrayLike}\\n * @implements {Iterable}\\n * @template T\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-536297177\\n */\\nfunction NodeList() {}\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-203510337\\n */\\nNodeList.prototype.length;\\n\\n/**\\n * @param {number} index\\n * @return {T|null}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-844377136\\n */\\nNodeList.prototype.item = function(index) {};\\n\\n/**\\n * @param {?function(this:S, T, number, !NodeList): ?} callback\\n * @param {S=} opt_thisobj\\n * @this {NodeList}\\n * @template T,S\\n * @return {undefined}\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach\\n */\\nNodeList.prototype.forEach = function(callback, opt_thisobj) {};\\n\\n/**\\n * @constructor\\n * @implements {IObject<(string|number), T>}\\n * @implements {IArrayLike}\\n * @implements {Iterable}\\n * @template T\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1780488922\\n */\\nfunction NamedNodeMap() {}\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-6D0FB19E\\n */\\nNamedNodeMap.prototype.length;\\n\\n/**\\n * @param {string} name\\n * @return {Node}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1074577549\\n * @nosideeffects\\n */\\nNamedNodeMap.prototype.getNamedItem = function(name) {};\\n\\n/**\\n * @param {number} index\\n * @return {Node}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-349467F9\\n * @nosideeffects\\n */\\nNamedNodeMap.prototype.item = function(index) {};\\n\\n/**\\n * @param {string} name\\n * @return {Node}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-D58B193\\n */\\nNamedNodeMap.prototype.removeNamedItem = function(name) {};\\n\\n/**\\n * @param {Node} arg\\n * @return {Node}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1025163788\\n */\\nNamedNodeMap.prototype.setNamedItem = function(arg) {};\\n\\n/**\\n * @constructor\\n * @extends {Node}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-FF21A306\\n */\\nfunction CharacterData() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-72AB8359\\n */\\nCharacterData.prototype.data;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-7D61178C\\n */\\nCharacterData.prototype.length;\\n\\n/**\\n * @param {string} arg\\n * @return {undefined}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-32791A2F\\n */\\nCharacterData.prototype.appendData = function(arg) {};\\n\\n/**\\n * @param {number} offset\\n * @param {number} count\\n * @return {undefined}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-7C603781\\n */\\nCharacterData.prototype.deleteData = function(offset, count) {};\\n\\n/**\\n * @param {number} offset\\n * @param {string} arg\\n * @return {undefined}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-3EDB695F\\n */\\nCharacterData.prototype.insertData = function(offset, arg) {};\\n\\n/**\\n * @param {number} offset\\n * @param {number} count\\n * @param {string} arg\\n * @return {undefined}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-E5CBA7FB\\n */\\nCharacterData.prototype.replaceData = function(offset, count, arg) {};\\n\\n/**\\n * @param {number} offset\\n * @param {number} count\\n * @return {string}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-6531BCCF\\n * @nosideeffects\\n */\\nCharacterData.prototype.substringData = function(offset, count) {};\\n\\n/**\\n * @constructor\\n * @extends {Node}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-637646024\\n */\\nfunction Attr() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1112119403\\n */\\nAttr.prototype.name;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-862529273\\n */\\nAttr.prototype.specified;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-221662474\\n */\\nAttr.prototype.value;\\n\\n/**\\n * @constructor\\n * @extends {Node}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-745549614\\n */\\nfunction Element() {}\\n\\n/**\\n * An Element always contains a non-null NamedNodeMap containing the attributes\\n * of this node.\\n * @type {!NamedNodeMap}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-attributes\\n */\\nElement.prototype.attributes;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-tagName\\n */\\nElement.prototype.tagName;\\n\\n/**\\n * @implicitCast\\n * @type {?}\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/className\\n * We type it as ? even though it is a string, because some SVG elements have\\n * className that is an object, which isn't a subtype of string.\\n * Alternative: TypeScript types this as string and types className on\\n * SVGElement as ?.\\n */\\nElement.prototype.className;\\n\\n/**\\n * @param {string} name\\n * @param {number?=} opt_flags\\n * @return {string}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-getAttribute\\n * @see http://msdn.microsoft.com/en-us/library/ms536429(VS.85).aspx\\n * @nosideeffects\\n */\\nElement.prototype.getAttribute = function(name, opt_flags) {};\\n\\n/**\\n * @param {string} name\\n * @return {Attr}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-getAttributeNode\\n * @nosideeffects\\n */\\nElement.prototype.getAttributeNode = function(name) {};\\n\\n/**\\n * @param {string} tagname\\n * @return {!NodeList}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1938918D\\n * @nosideeffects\\n */\\nElement.prototype.getElementsByTagName = function(tagname) {};\\n\\n/**\\n * @param {string} name\\n * @return {undefined}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-removeAttribute\\n */\\nElement.prototype.removeAttribute = function(name) {};\\n\\n/**\\n * @param {Attr} oldAttr\\n * @return {?Attr}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-removeAttributeNode\\n */\\nElement.prototype.removeAttributeNode = function(oldAttr) {};\\n\\n/**\\n * @param {string} name\\n * @param {string|number|boolean} value Values are converted to strings with\\n * ToString, so we accept number and boolean since both convert easily to\\n * strings.\\n * @return {undefined}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-setAttribute\\n */\\nElement.prototype.setAttribute = function(name, value) {};\\n\\n/**\\n * @param {Attr} newAttr\\n * @return {?Attr}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-setAttributeNode\\n */\\nElement.prototype.setAttributeNode = function(newAttr) {};\\n\\n// Event handlers\\n// The DOM level 3 spec has a good index of these\\n// http://www.w3.org/TR/DOM-Level-3-Events/#event-types\\n\\n/** @type {?function (Event)} */ Element.prototype.onabort;\\n/** @type {?function (Event)} */ Element.prototype.onbeforeinput;\\n/** @type {?function (Event)} */ Element.prototype.onbeforeunload;\\n/** @type {?function (Event)} */ Element.prototype.onblur;\\n/** @type {?function (Event)} */ Element.prototype.onchange;\\n/** @type {?function (Event)} */ Element.prototype.onclick;\\n/** @type {?function (Event)} */ Element.prototype.oncompositionstart;\\n/** @type {?function (Event)} */ Element.prototype.oncompositionupdate;\\n/** @type {?function (Event)} */ Element.prototype.oncompositionend;\\n/** @type {?function (Event)} */ Element.prototype.oncontextmenu;\\n/** @type {?function (Event)} */ Element.prototype.oncopy;\\n/** @type {?function (Event)} */ Element.prototype.oncut;\\n/** @type {?function (Event)} */ Element.prototype.ondblclick;\\n/** @type {?function (Event)} */ Element.prototype.onerror;\\n/** @type {?function (Event)} */ Element.prototype.onfocus;\\n/** @type {?function (Event)} */ Element.prototype.onfocusin;\\n/** @type {?function (Event)} */ Element.prototype.onfocusout;\\n/** @type {?function (Event)} */ Element.prototype.oninput;\\n/** @type {?function (Event)} */ Element.prototype.onkeydown;\\n/** @type {?function (Event)} */ Element.prototype.onkeypress;\\n/** @type {?function (Event)} */ Element.prototype.onkeyup;\\n/** @type {?function (Event)} */ Element.prototype.onload;\\n/** @type {?function (Event)} */ Element.prototype.onunload;\\n/** @type {?function (Event)} */ Element.prototype.onmousedown;\\n/** @type {?function (Event)} */ Element.prototype.onmousemove;\\n/** @type {?function (Event)} */ Element.prototype.onmouseout;\\n/** @type {?function (Event)} */ Element.prototype.onmouseover;\\n/** @type {?function (Event)} */ Element.prototype.onmouseup;\\n/** @type {?function (Event)} */ Element.prototype.onmousewheel;\\n/** @type {?function (Event)} */ Element.prototype.onpaste;\\n/** @type {?function (Event)} */ Element.prototype.onreset;\\n/** @type {?function (Event)} */ Element.prototype.onresize;\\n/** @type {?function (Event)} */ Element.prototype.onscroll;\\n/** @type {?function (Event)} */ Element.prototype.onselect;\\n/** @type {?function (Event=)} */ Element.prototype.onsubmit;\\n/** @type {?function (Event)} */ Element.prototype.ontextinput;\\n/** @type {?function (Event)} */ Element.prototype.onwheel;\\n\\n/**\\n * @constructor\\n * @extends {CharacterData}\\n * @param {string=} contents Optional textual content.\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1312295772\\n */\\nfunction Text(contents) {}\\n\\n/**\\n * @param {number} offset\\n * @return {Text}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-38853C1D\\n */\\nText.prototype.splitText = function(offset) {};\\n\\n/**\\n * @constructor\\n * @extends {CharacterData}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1728279322\\n */\\nfunction Comment() {}\\n\\n/**\\n * @constructor\\n * @extends {Text}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-667469212\\n */\\nfunction CDATASection() {}\\n\\n/**\\n * @constructor\\n * @extends {Node}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-412266927\\n */\\nfunction DocumentType() {}\\n\\n/**\\n * @type {NamedNodeMap}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1788794630\\n */\\nDocumentType.prototype.entities;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1844763134\\n */\\nDocumentType.prototype.name;\\n\\n/**\\n * @type {NamedNodeMap}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-D46829EF\\n */\\nDocumentType.prototype.notati"; +a.a+='ons;\\n\\n/**\\n * @constructor\\n * @extends {Node}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-5431D1B9\\n */\\nfunction Notation() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-54F2B4D0\\n */\\nNotation.prototype.publicId;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-E8AAB1D0\\n */\\nNotation.prototype.systemId;\\n\\n/**\\n * @constructor\\n * @extends {Node}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-527DCFF2\\n */\\nfunction Entity() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-D7303025\\n */\\nEntity.prototype.publicId;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-D7C29F3E\\n */\\nEntity.prototype.systemId;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-6ABAEB38\\n */\\nEntity.prototype.notationName;\\n\\n/**\\n * @constructor\\n * @extends {Node}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-11C98490\\n */\\nfunction EntityReference() {}\\n\\n/**\\n * @constructor\\n * @extends {Node}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1004215813\\n */\\nfunction ProcessingInstruction() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-837822393\\n */\\nProcessingInstruction.prototype.data;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1478689192\\n */\\nProcessingInstruction.prototype.target;\\n\\n\\n/**\\n * @constructor\\n * @implements {EventTarget}\\n */\\nfunction Window() {}\\nWindow.prototype.Window;\\n\\n/** @override */\\nWindow.prototype.addEventListener = function(type, listener, opt_options) {};\\n\\n/** @override */\\nWindow.prototype.removeEventListener = function(type, listener, opt_options) {};\\n\\n/** @override */\\nWindow.prototype.dispatchEvent = function(evt) {};\\n\\n/** @type {?function (Event)} */ Window.prototype.onabort;\\n/** @type {?function (Event)} */ Window.prototype.onbeforeunload;\\n/** @type {?function (Event)} */ Window.prototype.onblur;\\n/** @type {?function (Event)} */ Window.prototype.onchange;\\n/** @type {?function (Event)} */ Window.prototype.onclick;\\n/** @type {?function (Event)} */ Window.prototype.onclose;\\n/** @type {?function (Event)} */ Window.prototype.oncontextmenu;\\n/** @type {?function (Event)} */ Window.prototype.ondblclick;\\n/** @type {?function (Event)} */ Window.prototype.ondragdrop;\\n// onerror has a special signature.\\n// See\\n// https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror\\n/**\\n * @type {?function (string, string, number, number, !Error):?}\\n */\\nWindow.prototype.onerror;\\n/** @type {?function (Event)} */ Window.prototype.onfocus;\\n/** @type {?function (Event)} */ Window.prototype.onhashchange;\\n/** @type {?function (Event)} */ Window.prototype.onkeydown;\\n/** @type {?function (Event)} */ Window.prototype.onkeypress;\\n/** @type {?function (Event)} */ Window.prototype.onkeyup;\\n/** @type {?function (Event)} */ Window.prototype.onload;\\n/** @type {?function (Event)} */ Window.prototype.onmousedown;\\n/** @type {?function (Event)} */ Window.prototype.onmousemove;\\n/** @type {?function (Event)} */ Window.prototype.onmouseout;\\n/** @type {?function (Event)} */ Window.prototype.onmouseover;\\n/** @type {?function (Event)} */ Window.prototype.onmouseup;\\n/** @type {?function (Event)} */ Window.prototype.onmousewheel;\\n/** @type {?function (Event)} */ Window.prototype.onpaint;\\n/** @type {?function (Event)} */ Window.prototype.onpopstate;\\n/** @type {?function (Event)} */ Window.prototype.onreset;\\n/** @type {?function (Event)} */ Window.prototype.onresize;\\n/** @type {?function (Event)} */ Window.prototype.onscroll;\\n/** @type {?function (Event)} */ Window.prototype.onselect;\\n/** @type {?function (Event=)} */ Window.prototype.onsubmit;\\n/** @type {?function (Event)} */ Window.prototype.onunload;\\n/** @type {?function (Event)} */ Window.prototype.onwheel;\\n","externs/w3c_dom2.js":"/*\\n * Copyright 2008 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for W3C\'s DOM Level 2 specification.\\n * This file depends on w3c_dom1.js.\\n * The whole file has been fully type annotated.\\n * Created from\\n * http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html\\n *\\n * @externs\\n */\\n\\n/**\\n * @param {string} s id.\\n * @return {Element}\\n * @nosideeffects\\n * @see https://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/core.html#ID-getElBId\\n */\\nDocument.prototype.getElementById = function(s) {};\\n\\n/**\\n * @param {?string} namespaceURI\\n * @param {string} qualifiedName\\n * @param {string=} opt_typeExtension\\n * @return {!Element}\\n * @see https://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/core.html#ID-DocCrElNS\\n */\\nDocument.prototype.createElementNS =\\n function(namespaceURI, qualifiedName, opt_typeExtension) {};\\n\\n/**\\n * @param {?string} namespaceURI\\n * @param {string} qualifiedName\\n * @return {!Attr}\\n * @see https://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/core.html#ID-DocCrElNS\\n */\\nDocument.prototype.createAttributeNS =\\n function(namespaceURI, qualifiedName) {};\\n\\n/**\\n * @param {Node} root\\n * @param {number=} whatToShow\\n * @param {NodeFilter=} filter\\n * @param {boolean=} entityReferenceExpansion\\n * @return {!NodeIterator}\\n * @see https://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113/traversal.html#Traversal-Document\\n * @see https://dom.spec.whatwg.org/#interface-document\\n * @nosideeffects\\n */\\nDocument.prototype.createNodeIterator = function(\\n root, whatToShow, filter, entityReferenceExpansion) {};\\n\\n/**\\n * @param {Node} root\\n * @param {number=} whatToShow\\n * @param {NodeFilter=} filter\\n * @param {boolean=} entityReferenceExpansion\\n * @return {!TreeWalker}\\n * @see https://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113/traversal.html#Traversal-Document\\n * @see https://dom.spec.whatwg.org/#interface-document\\n * @nosideeffects\\n */\\nDocument.prototype.createTreeWalker = function(\\n root, whatToShow, filter, entityReferenceExpansion) {};\\n\\n/**\\n * @param {string} namespace\\n * @param {string} name\\n * @return {!NodeList}\\n * @nosideeffects\\n * @see https://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/core.html#ID-getElBTNNS\\n */\\nDocument.prototype.getElementsByTagNameNS = function(namespace, name) {};\\n\\n/**\\n * @param {Node} externalNode\\n * @param {boolean} deep\\n * @return {Node}\\n * @see https://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/core.html#Core-Document-importNode\\n */\\nDocument.prototype.importNode = function(externalNode, deep) {};\\n\\n/**\\n * @constructor\\n * @implements {IObject<(string|number),T>}\\n * @implements {IArrayLike}\\n * @implements {Iterable}\\n * @template T\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-75708506\\n */\\nfunction HTMLCollection() {}\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-40057551\\n */\\nHTMLCollection.prototype.length;\\n\\n/**\\n * @param {number} index\\n * @return {T|null}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-33262535\\n * @nosideeffects\\n */\\nHTMLCollection.prototype.item = function(index) {};\\n\\n/**\\n * @param {string} name\\n * @return {T|null}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-21069976\\n * @nosideeffects\\n */\\nHTMLCollection.prototype.namedItem = function(name) {};\\n\\n/**\\n * @constructor\\n * @extends {HTMLCollection}\\n * @see https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#htmloptionscollection\\n */\\nfunction HTMLOptionsCollection() {}\\n\\n/**\\n * @type {number}\\n * @see https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#dom-htmloptionscollection-length\\n * @nosideeffects\\n */\\nHTMLOptionsCollection.prototype.length;\\n\\n/**\\n * @param {HTMLOptionElement|HTMLOptGroupElement} element\\n * @param {HTMLElement|number=} before\\n * @return {undefined}\\n * @see https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#dom-htmloptionscollection-add\\n */\\nHTMLOptionsCollection.prototype.add = function(element, before) {};\\n\\n/**\\n * NOTE(tjgq): The HTMLOptionsCollection#item method is inherited from\\n * HTMLCollection, but it must be declared explicitly to work around an error\\n * when building a jsinterop library for GWT.\\n * @param {number} index\\n * @return {HTMLOptionElement}\\n * @override\\n * @nosideeffects\\n */\\nHTMLOptionsCollection.prototype.item = function(index) {};\\n\\n/**\\n * @param {number} index\\n * @return {undefined}\\n * @see https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#dom-htmloptionscollection-remove\\n */\\nHTMLOptionsCollection.prototype.remove = function(index) {};\\n\\n/**\\n * @constructor\\n * @extends {Document}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-26809268\\n */\\nfunction HTMLDocument() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-18446827\\n */\\nHTMLDocument.prototype.title;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-95229140\\n */\\nHTMLDocument.prototype.referrer;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-2250147\\n */\\nHTMLDocument.prototype.domain;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-46183437\\n */\\nHTMLDocument.prototype.URL;\\n\\n/**\\n * @type {!HTMLBodyElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-56360201\\n */\\nHTMLDocument.prototype.body;\\n\\n/**\\n * @type {!HTMLCollection}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-90379117\\n */\\nHTMLDocument.prototype.images;\\n\\n/**\\n * @type {!HTMLCollection}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-85113862\\n */\\nHTMLDocument.prototype.applets;\\n\\n/**\\n * @type {!HTMLCollection<(!HTMLAnchorElement|!HTMLAreaElement)>}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-7068919\\n */\\nHTMLDocument.prototype.links;\\n\\n/**\\n * @type {!HTMLCollection}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-1689064\\n */\\nHTMLDocument.prototype.forms;\\n\\n/**\\n * @type {!HTMLCollection}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-7577272\\n */\\nHTMLDocument.prototype.anchors;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-8747038\\n */\\nHTMLDocument.prototype.cookie;\\n\\n/**\\n * @param {string=} opt_mimeType\\n * @param {string=} opt_replace\\n * @return {undefined}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-72161170\\n * Even though official spec says \\"no parameters\\" some old browsers might take\\n * optional parameters: https://msdn.microsoft.com/en-us/library/ms536652(v=vs.85).aspx\\n * @override\\n */\\nHTMLDocument.prototype.open = function(opt_mimeType, opt_replace) {};\\n\\n/**\\n * @return {undefined}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-98948567\\n * @override\\n */\\nHTMLDocument.prototype.close = function() {};\\n\\n/**\\n * @param {!TrustedHTML|string} text\\n * @return {undefined}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-75233634\\n * @override\\n */\\nHTMLDocument.prototype.write = function(text) {};\\n\\n/**\\n * @param {!TrustedHTML|string} text\\n * @return {undefined}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-35318390\\n * @override\\n */\\nHTMLDocument.prototype.writeln = function(text) {};\\n\\n/**\\n * @param {string} elementName\\n * @return {!NodeList}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-71555259\\n * @nosideeffects\\n */\\nHTMLDocument.prototype.getElementsByName = function(elementName) {};\\n\\n\\n/** @typedef {{\\n createNodeIterator: function(Node, number=, NodeFilter=, boolean=) : NodeIterator,\\n createTreeWalker: function(Node, number=, NodeFilter=, boolean=) : TreeWalker\\n}} */\\nvar TraversalDocument;\\n\\n/**\\n * @record\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/traversal.html#Traversal-NodeFilter\\n*/\\nfunction NodeFilter() {}\\n\\n/* Constants for whatToShow */\\n/** @const {number} */ NodeFilter.SHOW_ALL;\\n/** @const {number} */ NodeFilter.SHOW_ATTRIBUTE;\\n/** @const {number} */ NodeFilter.SHOW_CDATA_SECTION;\\n/** @const {number} */ NodeFilter.SHOW_COMMENT;\\n/** @const {number} */ NodeFilter.SHOW_DOCUMENT;\\n/** @const {number} */ NodeFilter.SHOW_DOCUMENT_FRAGMENT;\\n/** @const {number} */ NodeFilter.SHOW_DOCUMENT_TYPE;\\n/** @const {number} */ NodeFilter.SHOW_ELEMENT;\\n/** @const {number} */ NodeFilter.SHOW_ENTITY;\\n/** @const {number} */ NodeFilter.SHOW_ENTITY_REFERENCE;\\n/** @const {number} */ NodeFilter.SHOW_NOTATION;\\n/** @const {number} */ NodeFilter.SHOW_PROCESSING_INSTRUCTION;\\n/** @const {number} */ NodeFilter.SHOW_TEXT;\\n\\n/* Consants for acceptNode */\\n/** @const {number} */ NodeFilter.FILTER_ACCEPT;\\n/** @const {number} */ NodeFilter.FILTER_REJECT;\\n/** @const {number} */ NodeFilter.FILTER_SKIP;\\n\\n/**\\n * @param {Node} n\\n * @return {number} Any of NodeFilter.FILTER_* constants.\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/traversal.html#Traversal-NodeFilter-acceptNode\\n*/\\nNodeFilter.prototype.acceptNode = function(n) {};\\n\\n/**\\n * @interface\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/traversal.html#Traversal-NodeIterator\\n*/\\nfunction NodeIterator() {}\\n\\n/**\\n * Detach and invalidate the NodeIterator.\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/traversal.html#Traversal-NodeIterator-detach\\n * @return {undefined}\\n */\\nNodeIterator.prototype.detach = function() {};\\n\\n/**\\n * @return {Node} Next node in the set.\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/traversal.html#Traversal-NodeIterator-nextNode\\n */\\nNodeIterator.prototype.nextNode = function() {};\\n\\n/**\\n * @return {Node} Previous node in the set.\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/traversal.html#Traversal-NodeIterator-previousNode\\n */\\nNodeIterator.prototype.previousNode = function() {};\\n\\n/**\\n * @interface\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/traversal.html#Traversal-TreeWalker\\n*/\\nfunction TreeWalker() {}\\n\\n/**\\n * @return {?Node} The new Node or null.\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/traversal.html#Traversal-TreeWalker-firstChild\\n */\\nTreeWalker.prototype.firstChild = function() {};\\n\\n/**\\n * @return {?Node} The new Node or null..\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/traversal.html#Traversal-TreeWalker-lastChild\\n */\\nTreeWalker.prototype.lastChild = function() {};\\n\\n/**\\n * @return {?Node} The new Node or null.\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/traversal.html#Traversal-TreeWalker-nextNode\\n */\\nTreeWalker.prototype.nextNode = function() {};\\n\\n/**\\n * @return {?Node} The new Node or null.\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/traversal.html#Traversal-TreeWalker-nextSibling\\n */\\nTreeWalker.prototype.nextSibling = function() {};\\n\\n/**\\n * @return {?Node} The new Node or null.\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/traversal.html#Traversal-TreeWalker-parentNode\\n */\\nTreeWalker.prototype.parentNode = function() {};\\n\\n/**\\n * @return {?Node} The new Node or null.\\n '; +a.a+="* @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/traversal.html#Traversal-TreeWalker-previousNode\\n */\\nTreeWalker.prototype.previousNode = function() {};\\n\\n/**\\n * @return {?Node} The new Node or null.\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/traversal.html#Traversal-TreeWalker-previousSibling\\n */\\nTreeWalker.prototype.previousSibling = function() {};\\n\\n/**\\n * @type {Node}\\n */\\nTreeWalker.prototype.root;\\n\\n/**\\n * @type {number}\\n */\\nTreeWalker.prototype.whatToShow;\\n\\n/**\\n * @type {NodeFilter}\\n */\\nTreeWalker.prototype.filter;\\n\\n/**\\n * @type {boolean}\\n */\\nTreeWalker.prototype.expandEntityReference;\\n\\n/**\\n * @type {Node}\\n */\\nTreeWalker.prototype.currentNode;\\n\\n/**\\n * @constructor\\n * @extends {Element}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-58190037\\n */\\nfunction HTMLElement() {}\\n\\n/**\\n * @implicitCast\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-63534901\\n */\\nHTMLElement.prototype.id;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-78276800\\n */\\nHTMLElement.prototype.title;\\n\\n/**\\n * @type {!CSSStyleDeclaration}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-ElementCSSInlineStyle\\n */\\nHTMLElement.prototype.style;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-59132807\\n */\\nHTMLElement.prototype.lang;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-52460740\\n */\\nHTMLElement.prototype.dir;\\n\\n/**\\n * @implicitCast\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-95362176\\n */\\nHTMLElement.prototype.className;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-40676705\\n */\\nHTMLElement.prototype.tabIndex;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-33759296\\n */\\nfunction HTMLHtmlElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-9383775\\n */\\nHTMLHtmlElement.prototype.version;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-77253168\\n */\\nfunction HTMLHeadElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-96921909\\n */\\nHTMLHeadElement.prototype.profile;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @implements {LinkStyle}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-35143001\\n */\\nfunction HTMLLinkElement() {}\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-87355129\\n */\\nHTMLLinkElement.prototype.disabled;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-63954491\\n */\\nHTMLLinkElement.prototype.charset;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-33532588\\n */\\nHTMLLinkElement.prototype.href;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-85145682\\n */\\nHTMLLinkElement.prototype.hreflang;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-75813125\\n */\\nHTMLLinkElement.prototype.media;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-41369587\\n */\\nHTMLLinkElement.prototype.rel;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-40715461\\n */\\nHTMLLinkElement.prototype.rev;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-84183095\\n */\\nHTMLLinkElement.prototype.target;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-32498296\\n */\\nHTMLLinkElement.prototype.type;\\n\\n/** @type {StyleSheet} */\\nHTMLLinkElement.prototype.sheet;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-79243169\\n */\\nfunction HTMLTitleElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-77500413\\n */\\nHTMLTitleElement.prototype.text;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-37041454\\n */\\nfunction HTMLMetaElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-87670826\\n */\\nHTMLMetaElement.prototype.content;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-77289449\\n */\\nHTMLMetaElement.prototype.httpEquiv;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-31037081\\n */\\nHTMLMetaElement.prototype.name;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-35993789\\n */\\nHTMLMetaElement.prototype.scheme;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-73629039\\n */\\nfunction HTMLBaseElement() {}\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-65382887\\n */\\nHTMLBaseElement.prototype.href;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-73844298\\n */\\nHTMLBaseElement.prototype.target;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-85283003\\n */\\nfunction HTMLIsIndexElement() {}\\n\\n/**\\n * @type {HTMLFormElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-87069980\\n */\\nHTMLIsIndexElement.prototype.form;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-33589862\\n */\\nHTMLIsIndexElement.prototype.prompt;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @implements {LinkStyle}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-16428977\\n */\\nfunction HTMLStyleElement() {}\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-51162010\\n */\\nHTMLStyleElement.prototype.disabled;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-76412738\\n */\\nHTMLStyleElement.prototype.media;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-22472002\\n */\\nHTMLStyleElement.prototype.type;\\n\\n/** @type {StyleSheet} */\\nHTMLStyleElement.prototype.sheet;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-62018039\\n */\\nfunction HTMLBodyElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-59424581\\n */\\nHTMLBodyElement.prototype.aLink;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-37574810\\n */\\nHTMLBodyElement.prototype.background;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-24940084\\n */\\nHTMLBodyElement.prototype.bgColor;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-7662206\\n */\\nHTMLBodyElement.prototype.link;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-73714763\\n */\\nHTMLBodyElement.prototype.text;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-83224305\\n */\\nHTMLBodyElement.prototype.vLink;\\n\\n/**\\n * @constructor\\n * @extends {HTMLCollection}\\n * @implements {IObject)>}\\n * @implements {IArrayLike}\\n * @template T\\n * @see https://html.spec.whatwg.org/multipage/infrastructure.html#the-htmlformcontrolscollection-interface\\n */\\nfunction HTMLFormControlsCollection() {}\\n\\n/**\\n * @param {string} name\\n * @return {T|RadioNodeList|null}\\n * @see https://html.spec.whatwg.org/multipage/infrastructure.html#dom-htmlformcontrolscollection-nameditem\\n * @nosideeffects\\n * @override\\n * @suppress {newCheckTypes}\\n */\\nHTMLFormControlsCollection.prototype.namedItem = function(name) {};\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-40002357\\n */\\nfunction HTMLFormElement() {}\\n\\n/**\\n * @type {!HTMLFormControlsCollection}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-76728479\\n */\\nHTMLFormElement.prototype.elements;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#HTML-HTMLFormElement-length\\n */\\nHTMLFormElement.prototype.length;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-22051454\\n */\\nHTMLFormElement.prototype.name;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-19661795\\n */\\nHTMLFormElement.prototype.acceptCharset;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-74049184\\n */\\nHTMLFormElement.prototype.action;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-84227810\\n */\\nHTMLFormElement.prototype.enctype;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-82545539\\n */\\nHTMLFormElement.prototype.method;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-6512890\\n */\\nHTMLFormElement.prototype.target;\\n\\n/**\\n * @return {undefined}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-76767676\\n */\\nHTMLFormElement.prototype.submit = function() {};\\n\\n/**\\n * @return {undefined}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-76767677\\n */\\nHTMLFormElement.prototype.reset = function() {};\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-94282980\\n */\\nfunction HTMLSelectElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-58783172\\n */\\nHTMLSelectElement.prototype.type;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-85676760\\n */\\nHTMLSelectElement.prototype.selectedIndex;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-59351919\\n */\\nHTMLSelectElement.prototype.value;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-5933486\\n */\\nHTMLSelectElement.prototype.length;\\n\\n/**\\n * @type {HTMLFormElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-20489458\\n */\\nHTMLSelectElement.prototype.form;\\n\\n/**\\n * @type {!HTMLOptionsCollection}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-30606413\\n */\\nHTMLSelectElement.prototype.options;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-79102918\\n */\\nHTMLSelectElement.prototype.disabled;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-13246613\\n */\\nHTMLSelectElement.prototype.multiple;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-41636323\\n */\\nHTMLSelectElement.prototype.name;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-18293826\\n */\\nHTMLSelectElement.prototype.size;\\n\\n/**\\n * @param {HTMLElement} element\\n * @param {HTMLElement=} opt_before\\n * @return {undefined}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-14493106\\n */\\nHTMLSelectElement.prototype.add = function(element, opt_before) {};\\n\\n/**\\n * @return {undefined}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-28216144\\n * @override\\n */\\nHTMLSelectElement.prototype.blur = function() {};\\n\\n/**\\n * @return {undefined}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-32130014\\n * @override\\n */\\nHTMLSelectElement.prototype.focus = function() {};\\n\\n/**\\n * @param {number=} opt_index\\n * @return {undefined}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-33404570\\n * @override\\n */\\nHTMLSelectElement.prototype.remove = function(opt_index) {};\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-38450247\\n */\\nfunction HTMLOptGroupElement() {}\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-15518803\\n */\\nHTMLOptGroupElement.prototype.disabled;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-95806054\\n */\\nHTMLOptGroupElement.prototype.label;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-70901257\\n */\\nfunction HTMLOptionElement() {}\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-37770574\\n */\\nHTMLOptionElement.prototype.defaultSelected;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-23482473\\n */\\nHTMLOptionElement.prototype.disabled;\\n\\n/**\\n * @type {HTMLFormElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-17116503\\n */\\nHTMLOptionElement.prototype.form;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-14038413\\n */\\nHTMLOptionElement.prototype.index;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-40736115\\n */\\nHTMLOptionElement.prototype.label;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-70874476\\n */\\nHTMLOptionElement.prototype.selected;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-48154426\\n */\\nHTMLOptionElement.prototype.text;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-6185554\\n */\\nHTMLOptionElement.prototype.value;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-6043025\\n */\\nfunction HTMLInputElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-15328520\\n */\\nHTMLInputElement.prototype.accept;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-59914154\\n */\\nHTMLInputElement.prototype.accessKey;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-96991182\\n */\\nHTMLInputElement.prototype.align;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-92701314\\n */\\nHTMLInputElement.prototype.alt;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-30233917\\n */\\nHTMLInputElement.prototype.checked;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-20509171\\n */\\nHTMLInputElement.prototype.defaultChecked;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-26091157\\n */\\nHTMLInputElement.prototype.defaultValue;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-50886781\\n */\\nHTMLInputElement.prototype.disabled;\\n\\n/**\\n * @type {HTMLFormElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-63239895\\n */\\nHTMLInputElement.prototype.form;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-54719353\\n */\\nHTMLInp"; +a.a+="utElement.prototype.maxLength;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-89658498\\n */\\nHTMLInputElement.prototype.name;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-88461592\\n */\\nHTMLInputElement.prototype.readOnly;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-79659438\\n */\\nHTMLInputElement.prototype.size;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-97320704\\n */\\nHTMLInputElement.prototype.src;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-62176355\\n */\\nHTMLInputElement.prototype.tabIndex;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-62883744\\n */\\nHTMLInputElement.prototype.type;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-32463706\\n */\\nHTMLInputElement.prototype.useMap;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-49531485\\n */\\nHTMLInputElement.prototype.value;\\n\\n/**\\n * @return {undefined}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-26838235\\n * @override\\n */\\nHTMLInputElement.prototype.blur = function() {};\\n\\n/**\\n * @return {undefined}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-2651361\\n * @override\\n */\\nHTMLInputElement.prototype.click = function() {};\\n\\n/**\\n * @return {undefined}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-65996295\\n * @override\\n */\\nHTMLInputElement.prototype.focus = function() {};\\n\\n/**\\n * @return {undefined}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-34677168\\n */\\nHTMLInputElement.prototype.select = function() {};\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-24874179\\n */\\nfunction HTMLTextAreaElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-93102991\\n */\\nHTMLTextAreaElement.prototype.accessKey;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-51387225\\n */\\nHTMLTextAreaElement.prototype.cols;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-36152213\\n */\\nHTMLTextAreaElement.prototype.defaultValue;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-98725443\\n */\\nHTMLTextAreaElement.prototype.disabled;\\n\\n/**\\n * @type {HTMLFormElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-18911464\\n */\\nHTMLTextAreaElement.prototype.form;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-70715578\\n */\\nHTMLTextAreaElement.prototype.name;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-39131423\\n */\\nHTMLTextAreaElement.prototype.readOnly;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-46975887\\n */\\nHTMLTextAreaElement.prototype.rows;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-60363303\\n */\\nHTMLTextAreaElement.prototype.tabIndex;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#HTML-HTMLTextAreaElement-type\\n */\\nHTMLTextAreaElement.prototype.type;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-70715579\\n */\\nHTMLTextAreaElement.prototype.value;\\n\\n/**\\n * @return {undefined}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-6750689\\n * @override\\n */\\nHTMLTextAreaElement.prototype.blur = function() {};\\n\\n/**\\n * @return {undefined}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-39055426\\n * @override\\n */\\nHTMLTextAreaElement.prototype.focus = function() {};\\n\\n/**\\n * @return {undefined}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-48880622\\n */\\nHTMLTextAreaElement.prototype.select = function() {};\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-34812697\\n */\\nfunction HTMLButtonElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-73169431\\n */\\nHTMLButtonElement.prototype.accessKey;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-92757155\\n */\\nHTMLButtonElement.prototype.disabled;\\n\\n/**\\n * @type {HTMLFormElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-71254493\\n */\\nHTMLButtonElement.prototype.form;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-11029910\\n */\\nHTMLButtonElement.prototype.name;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-39190908\\n */\\nHTMLButtonElement.prototype.tabIndex;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-27430092\\n */\\nHTMLButtonElement.prototype.type;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-72856782\\n */\\nHTMLButtonElement.prototype.value;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-13691394\\n */\\nfunction HTMLLabelElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-43589892\\n */\\nHTMLLabelElement.prototype.accessKey;\\n\\n/**\\n * @type {HTMLFormElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-32480901\\n */\\nHTMLLabelElement.prototype.form;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-96509813\\n */\\nHTMLLabelElement.prototype.htmlFor;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-7365882\\n */\\nfunction HTMLFieldSetElement() {}\\n\\n/**\\n * @type {HTMLFormElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-75392630\\n */\\nHTMLFieldSetElement.prototype.form;\\n\\n/**\\n * @type {boolean}\\n * @see https://www.w3.org/TR/html5/forms.html#attr-fieldset-disabled\\n */\\nHTMLFieldSetElement.prototype.disabled;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-21482039\\n */\\nfunction HTMLLegendElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-11297832\\n */\\nHTMLLegendElement.prototype.accessKey;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-79538067\\n */\\nHTMLLegendElement.prototype.align;\\n\\n/**\\n * @type {HTMLFormElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-29594519\\n */\\nHTMLLegendElement.prototype.form;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-86834457\\n */\\nfunction HTMLUListElement() {}\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-39864178\\n */\\nHTMLUListElement.prototype.compact;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-96874670\\n */\\nHTMLUListElement.prototype.type;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-58056027\\n */\\nfunction HTMLOListElement() {}\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-76448506\\n */\\nHTMLOListElement.prototype.compact;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-14793325\\n */\\nHTMLOListElement.prototype.start;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-40971103\\n */\\nHTMLOListElement.prototype.type;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-52368974\\n */\\nfunction HTMLDListElement() {}\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-21738539\\n */\\nHTMLDListElement.prototype.compact;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-71600284\\n */\\nfunction HTMLDirectoryElement() {}\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-75317739\\n */\\nHTMLDirectoryElement.prototype.compact;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-72509186\\n * @see http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html#the-menu-element\\n */\\nfunction HTMLMenuElement() {}\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-68436464\\n */\\nHTMLMenuElement.prototype.compact;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-74680021\\n */\\nfunction HTMLLIElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-52387668\\n */\\nHTMLLIElement.prototype.type;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-45496263\\n */\\nHTMLLIElement.prototype.value;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-22445964\\n */\\nfunction HTMLDivElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-70908791\\n */\\nHTMLDivElement.prototype.align;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-84675076\\n */\\nfunction HTMLParagraphElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-53465507\\n */\\nHTMLParagraphElement.prototype.align;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-43345119\\n */\\nfunction HTMLHeadingElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-6796462\\n */\\nHTMLHeadingElement.prototype.align;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-70319763\\n */\\nfunction HTMLQuoteElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-53895598\\n */\\nHTMLQuoteElement.prototype.cite;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-11383425\\n */\\nfunction HTMLPreElement() {}\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-13894083\\n */\\nHTMLPreElement.prototype.width;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-56836063\\n */\\nfunction HTMLBRElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-82703081\\n */\\nHTMLBRElement.prototype.clear;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-32774408\\n */\\nfunction HTMLBaseFontElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-87502302\\n */\\nHTMLBaseFontElement.prototype.color;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-88128969\\n */\\nHTMLBaseFontElement.prototype.face;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-38930424\\n */\\nHTMLBaseFontElement.prototype.size;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-43943847\\n */\\nfunction HTMLFontElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-53532975\\n */\\nHTMLFontElement.prototype.color;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-55715655\\n */\\nHTMLFontElement.prototype.face;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-90127284\\n */\\nHTMLFontElement.prototype.size;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-68228811\\n */\\nfunction HTMLHRElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-15235012\\n */\\nHTMLHRElement.prototype.align;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-79813978\\n */\\nHTMLHRElement.prototype.noShade;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-77612587\\n */\\nHTMLHRElement.prototype.size;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-87744198\\n */\\nHTMLHRElement.prototype.width;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-79359609\\n */\\nfunction HTMLModElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-75101708\\n */\\nHTMLModElement.prototype.cite;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-88432678\\n */\\nHTMLModElement.prototype.dateTime;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-48250443\\n */\\nfunction HTMLAnchorElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-89647724\\n */\\nHTMLAnchorElement.prototype.accessKey;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-67619266\\n */\\nHTMLAnchorElement.prototype.charset;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-92079539\\n */\\nHTMLAnchorElement.prototype.coords;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-88517319\\n */\\nHTMLAnchorElement.prototype.href;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-87358513\\n */\\nHTMLAnchorElement.prototype.hreflang;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-32783304\\n */\\nHTMLAnchorElement.prototype.name;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-3815891\\n */\\nHTMLAnchorElement.prototype.rel;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-58259771\\n */\\nHTMLAnchorElement.prototype.rev;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-49899808\\n */\\nHTMLAnchorElement.prototype.shape;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-41586466\\n */\\nHTMLAnchorElement.prototype.tabIndex;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-6414197\\n */\\nHTMLAnchorElement.prototype.target;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-63938221\\n */\\nHTMLAnchorElement.prototype.type;\\n\\n/**\\n * @return {undefined}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-65068939\\n * @override\\n */\\nHTMLAnchorElement.prototyp"; +a.a+="e.blur = function() {};\\n\\n/**\\n * @return {undefined}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-47150313\\n * @override\\n */\\nHTMLAnchorElement.prototype.focus = function() {};\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-17701901\\n */\\nfunction HTMLImageElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-3211094\\n */\\nHTMLImageElement.prototype.align;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-95636861\\n */\\nHTMLImageElement.prototype.alt;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-136671\\n */\\nHTMLImageElement.prototype.border;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-91561496\\n */\\nHTMLImageElement.prototype.height;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-53675471\\n */\\nHTMLImageElement.prototype.hspace;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-58983880\\n */\\nHTMLImageElement.prototype.isMap;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-77376969\\n */\\nHTMLImageElement.prototype.longDesc;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-91256910\\n */\\nHTMLImageElement.prototype.lowSrc;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-47534097\\n */\\nHTMLImageElement.prototype.name;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-87762984\\n */\\nHTMLImageElement.prototype.src;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-35981181\\n */\\nHTMLImageElement.prototype.useMap;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-85374897\\n */\\nHTMLImageElement.prototype.vspace;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-13839076\\n */\\nHTMLImageElement.prototype.width;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-9893177\\n */\\nfunction HTMLObjectElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-16962097\\n */\\nHTMLObjectElement.prototype.align;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-47783837\\n */\\nHTMLObjectElement.prototype.archive;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-82818419\\n */\\nHTMLObjectElement.prototype.border;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-75241146\\n */\\nHTMLObjectElement.prototype.code;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-25709136\\n */\\nHTMLObjectElement.prototype.codeBase;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-19945008\\n */\\nHTMLObjectElement.prototype.codeType;\\n\\n/**\\n * @type {Document}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-38538621\\n */\\nHTMLObjectElement.prototype.contentDocument;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-81766986\\n */\\nHTMLObjectElement.prototype.data;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-942770\\n */\\nHTMLObjectElement.prototype.declare;\\n\\n/**\\n * @type {HTMLFormElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-46094773\\n */\\nHTMLObjectElement.prototype.form;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-88925838\\n */\\nHTMLObjectElement.prototype.height;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-17085376\\n */\\nHTMLObjectElement.prototype.hspace;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-20110362\\n */\\nHTMLObjectElement.prototype.name;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-25039673\\n */\\nHTMLObjectElement.prototype.standby;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-27083787\\n */\\nHTMLObjectElement.prototype.tabIndex;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-91665621\\n */\\nHTMLObjectElement.prototype.type;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-6649772\\n */\\nHTMLObjectElement.prototype.useMap;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-8682483\\n */\\nHTMLObjectElement.prototype.vspace;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-38538620\\n */\\nHTMLObjectElement.prototype.width;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-64077273\\n */\\nfunction HTMLParamElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-59871447\\n */\\nHTMLParamElement.prototype.name;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-18179888\\n */\\nHTMLParamElement.prototype.type;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-77971357\\n */\\nHTMLParamElement.prototype.value;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-23931872\\n */\\nHTMLParamElement.prototype.valueType;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-31006348\\n */\\nfunction HTMLAppletElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-8049912\\n */\\nHTMLAppletElement.prototype.align;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-58610064\\n */\\nHTMLAppletElement.prototype.alt;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-14476360\\n */\\nHTMLAppletElement.prototype.archive;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-61509645\\n */\\nHTMLAppletElement.prototype.code;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-6581160\\n */\\nHTMLAppletElement.prototype.codeBase;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-90184867\\n */\\nHTMLAppletElement.prototype.height;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-1567197\\n */\\nHTMLAppletElement.prototype.hspace;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-39843695\\n */\\nHTMLAppletElement.prototype.name;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-93681523\\n */\\nHTMLAppletElement.prototype.object;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-22637173\\n */\\nHTMLAppletElement.prototype.vspace;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-16526327\\n */\\nHTMLAppletElement.prototype.width;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-94109203\\n */\\nfunction HTMLMapElement() {}\\n\\n/**\\n * @type {HTMLCollection}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-71838730\\n */\\nHTMLMapElement.prototype.areas;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-52696514\\n */\\nHTMLMapElement.prototype.name;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-26019118\\n */\\nfunction HTMLAreaElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-57944457\\n */\\nHTMLAreaElement.prototype.accessKey;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-39775416\\n */\\nHTMLAreaElement.prototype.alt;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-66021476\\n */\\nHTMLAreaElement.prototype.coords;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-34672936\\n */\\nHTMLAreaElement.prototype.href;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-61826871\\n */\\nHTMLAreaElement.prototype.noHref;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-85683271\\n */\\nHTMLAreaElement.prototype.shape;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-8722121\\n */\\nHTMLAreaElement.prototype.tabIndex;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-46054682\\n */\\nHTMLAreaElement.prototype.target;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-81598695\\n */\\nfunction HTMLScriptElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-35305677\\n */\\nHTMLScriptElement.prototype.charset;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-93788534\\n */\\nHTMLScriptElement.prototype.defer;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-56700403\\n */\\nHTMLScriptElement.prototype.event;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-66979266\\n */\\nHTMLScriptElement.prototype.htmlFor;\\n\\n/**\\n * @type {?function(!Event)}\\n */\\nHTMLScriptElement.prototype.onreadystatechange;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-75147231\\n */\\nHTMLScriptElement.prototype.src;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-46872999\\n */\\nHTMLScriptElement.prototype.text;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-30534818\\n */\\nHTMLScriptElement.prototype.type;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-64060425\\n */\\nfunction HTMLTableElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-23180977\\n */\\nHTMLTableElement.prototype.align;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-83532985\\n */\\nHTMLTableElement.prototype.bgColor;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-50969400\\n */\\nHTMLTableElement.prototype.border;\\n\\n/**\\n * @type {HTMLTableCaptionElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-14594520\\n */\\nHTMLTableElement.prototype.caption;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-59162158\\n */\\nHTMLTableElement.prototype.cellPadding;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-68907883\\n */\\nHTMLTableElement.prototype.cellSpacing;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-64808476\\n */\\nHTMLTableElement.prototype.frame;\\n\\n/**\\n * @type {HTMLCollection}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-6156016\\n */\\nHTMLTableElement.prototype.rows;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-26347553\\n */\\nHTMLTableElement.prototype.rules;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-44998528\\n */\\nHTMLTableElement.prototype.summary;\\n\\n/**\\n * @type {HTMLCollection}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-63206416\\n */\\nHTMLTableElement.prototype.tBodies;\\n\\n/**\\n * @type {HTMLTableSectionElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-64197097\\n */\\nHTMLTableElement.prototype.tFoot;\\n\\n/**\\n * @type {HTMLTableSectionElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-9530944\\n */\\nHTMLTableElement.prototype.tHead;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-77447361\\n */\\nHTMLTableElement.prototype.width;\\n\\n/**\\n * @return {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-96920263\\n */\\nHTMLTableElement.prototype.createCaption = function() {};\\n\\n/**\\n * @return {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-8453710\\n */\\nHTMLTableElement.prototype.createTFoot = function() {};\\n\\n/**\\n * @return {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-70313345\\n */\\nHTMLTableElement.prototype.createTHead = function() {};\\n\\n/**\\n * @return {undefined}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-22930071\\n */\\nHTMLTableElement.prototype.deleteCaption = function() {};\\n\\n/**\\n * @param {number} index\\n * @return {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-13114938\\n */\\nHTMLTableElement.prototype.deleteRow = function(index) {};\\n\\n/**\\n * @return {undefined}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-78363258\\n */\\nHTMLTableElement.prototype.deleteTFoot = function() {};\\n\\n/**\\n * @return {undefined}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-38310198\\n */\\nHTMLTableElement.prototype.deleteTHead = function() {};\\n\\n/**\\n * @param {number=} opt_index\\n * @return {HTMLElement}\\n * @see https://www.w3.org/TR/html5/tabular-data.html#htmltableelement\\n */\\nHTMLTableElement.prototype.insertRow = function(opt_index) {};\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-12035137\\n */\\nfunction HTMLTableCaptionElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-79875068\\n */\\nHTMLTableCaptionElement.prototype.align;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-84150186\\n */\\nfunction HTMLTableColElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-31128447\\n */\\nHTMLTableColElement.prototype.align;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-9447412\\n */\\nHTMLTableColElement.prototype.ch;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-57779225\\n */\\nHTMLTableColElement.prototype.chOff;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-96511335\\n */\\nHTMLTableColElement.prototype.span;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-83291710\\n */\\nHTMLTableColElement.prototype.vAlign;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-25196799\\n */"; +a.a+='\\nHTMLTableColElement.prototype.width;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-67417573\\n */\\nfunction HTMLTableSectionElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-40530119\\n */\\nHTMLTableSectionElement.prototype.align;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-83470012\\n */\\nHTMLTableSectionElement.prototype.ch;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-53459732\\n */\\nHTMLTableSectionElement.prototype.chOff;\\n\\n/**\\n * @type {HTMLCollection}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-52092650\\n */\\nHTMLTableSectionElement.prototype.rows;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-4379116\\n */\\nHTMLTableSectionElement.prototype.vAlign;\\n\\n/**\\n * @param {number} index\\n * @return {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-5625626\\n */\\nHTMLTableSectionElement.prototype.deleteRow = function(index) {};\\n\\n/**\\n * @param {number=} opt_index\\n * @return {HTMLElement}\\n * @see https://www.w3.org/TR/html5/tabular-data.html#htmltablesectionelement\\n */\\nHTMLTableSectionElement.prototype.insertRow = function(opt_index) {};\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-6986576\\n */\\nfunction HTMLTableRowElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-74098257\\n */\\nHTMLTableRowElement.prototype.align;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-18161327\\n */\\nHTMLTableRowElement.prototype.bgColor;\\n\\n/**\\n * @type {HTMLCollection}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-67349879\\n */\\nHTMLTableRowElement.prototype.cells;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-16230502\\n */\\nHTMLTableRowElement.prototype.ch;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-68207461\\n */\\nHTMLTableRowElement.prototype.chOff;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-67347567\\n */\\nHTMLTableRowElement.prototype.rowIndex;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-79105901\\n */\\nHTMLTableRowElement.prototype.sectionRowIndex;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-90000058\\n */\\nHTMLTableRowElement.prototype.vAlign;\\n\\n/**\\n * @param {number} index\\n * @return {undefined}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-11738598\\n */\\nHTMLTableRowElement.prototype.deleteCell = function(index) {};\\n\\n/**\\n * @param {number} index\\n * @return {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-68927016\\n */\\nHTMLTableRowElement.prototype.insertCell = function(index) {};\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-82915075\\n */\\nfunction HTMLTableCellElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-74444037\\n */\\nHTMLTableCellElement.prototype.abbr;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-98433879\\n */\\nHTMLTableCellElement.prototype.align;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-76554418\\n */\\nHTMLTableCellElement.prototype.axis;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-88135431\\n */\\nHTMLTableCellElement.prototype.bgColor;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-80748363\\n */\\nHTMLTableCellElement.prototype.cellIndex;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-30914780\\n */\\nHTMLTableCellElement.prototype.ch;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-20144310\\n */\\nHTMLTableCellElement.prototype.chOff;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-84645244\\n */\\nHTMLTableCellElement.prototype.colSpan;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-89104817\\n */\\nHTMLTableCellElement.prototype.headers;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-83679212\\n */\\nHTMLTableCellElement.prototype.height;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-62922045\\n */\\nHTMLTableCellElement.prototype.noWrap;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-48237625\\n */\\nHTMLTableCellElement.prototype.rowSpan;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-36139952\\n */\\nHTMLTableCellElement.prototype.scope;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-58284221\\n */\\nHTMLTableCellElement.prototype.vAlign;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-27480795\\n */\\nHTMLTableCellElement.prototype.width;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-43829095\\n */\\nfunction HTMLFrameSetElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-98869594\\n */\\nHTMLFrameSetElement.prototype.cols;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-19739247\\n */\\nHTMLFrameSetElement.prototype.rows;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-97790553\\n */\\nfunction HTMLFrameElement() {}\\n\\n/**\\n * @type {Document}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-78799536\\n */\\nHTMLFrameElement.prototype.contentDocument;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-11858633\\n */\\nHTMLFrameElement.prototype.frameBorder;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-7836998\\n */\\nHTMLFrameElement.prototype.longDesc;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-55569778\\n */\\nHTMLFrameElement.prototype.marginHeight;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-8369969\\n */\\nHTMLFrameElement.prototype.marginWidth;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-91128709\\n */\\nHTMLFrameElement.prototype.name;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-80766578\\n */\\nHTMLFrameElement.prototype.noResize;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-45411424\\n */\\nHTMLFrameElement.prototype.scrolling;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-78799535\\n */\\nHTMLFrameElement.prototype.src;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-50708718\\n */\\nfunction HTMLIFrameElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-11309947\\n */\\nHTMLIFrameElement.prototype.align;\\n\\n/**\\n * @type {Document}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-67133006\\n */\\nHTMLIFrameElement.prototype.contentDocument;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-22463410\\n */\\nHTMLIFrameElement.prototype.frameBorder;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-1678118\\n */\\nHTMLIFrameElement.prototype.height;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-70472105\\n */\\nHTMLIFrameElement.prototype.longDesc;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-91371294\\n */\\nHTMLIFrameElement.prototype.marginHeight;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-66486595\\n */\\nHTMLIFrameElement.prototype.marginWidth;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-96819659\\n */\\nHTMLIFrameElement.prototype.name;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-36369822\\n */\\nHTMLIFrameElement.prototype.scrolling;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-43933957\\n */\\nHTMLIFrameElement.prototype.src;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-67133005\\n */\\nHTMLIFrameElement.prototype.width;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-258A00AF\\n */\\nDOMException.INVALID_STATE_ERR = 11;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-258A00AF\\n */\\nDOMException.SYNTAX_ERR = 12;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-258A00AF\\n */\\nDOMException.INVALID_MODIFICATION_ERR = 13;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-258A00AF\\n */\\nDOMException.NAMESPACE_ERR = 14;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-258A00AF\\n */\\nDOMException.INVALID_ACCESS_ERR = 15;\\n","externs/w3c_dom3.js":"/*\\n * Copyright 2008 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for W3C\'s DOM Level 3 specification.\\n * This file depends on w3c_dom2.js.\\n * The whole file has been fully type annotated.\\n * Created from\\n * http://www.w3.org/TR/DOM-Level-3-Core/ecma-script-binding.html\\n *\\n * @externs\\n * @author acleung@google.com (Alan Leung)\\n * @author stevey@google.com (Steve Yegge)\\n */\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-258A00AF\\n */\\nDOMException.prototype.code;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-258A00AF\\n */\\nDOMException.VALIDATION_ERR = 16;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-258A00AF\\n */\\nDOMException.TYPE_MISMATCH_ERR = 17;\\n\\n/**\\n * @constructor\\n * @implements {IArrayLike}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMStringList\\n */\\nfunction DOMStringList() {}\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMStringList-length\\n */\\nDOMStringList.prototype.length;\\n\\n/**\\n * @param {string} str\\n * @return {boolean}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMStringList-contains\\n * @nosideeffects\\n */\\nDOMStringList.prototype.contains = function(str) {};\\n\\n/**\\n * @param {number} index\\n * @return {string}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMStringList-item\\n * @nosideeffects\\n */\\nDOMStringList.prototype.item = function(index) {};\\n\\n/**\\n * @constructor\\n * @implements {IArrayLike}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#NameList\\n */\\nfunction NameList() {}\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#NameList-length\\n */\\nNameList.prototype.length;\\n\\n/**\\n * @param {string} str\\n * @return {boolean}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#NameList-contains\\n * @nosideeffects\\n */\\nNameList.prototype.contains = function(str) {};\\n\\n/**\\n * @param {?string} namespaceURI\\n * @param {string} name\\n * @return {boolean}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#NameList-containsNS\\n * @nosideeffects\\n */\\nNameList.prototype.containsNS = function(namespaceURI, name) {};\\n\\n/**\\n * @param {number} index\\n * @return {string}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#NameList-getName\\n * @nosideeffects\\n */\\nNameList.prototype.getName = function(index) {};\\n\\n/**\\n * @param {number} index\\n * @return {string}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#NameList-getNamespaceURI\\n * @nosideeffects\\n */\\nNameList.prototype.getNamespaceURI = function(index) {};\\n\\n/**\\n * @constructor\\n * @implements {IArrayLike}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMImplementationList\\n */\\nfunction DOMImplementationList() {}\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMImplementationList-length\\n */\\nDOMImplementationList.prototype.length;\\n\\n/**\\n * @param {number} index\\n * @return {DOMImplementation}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMImplementationList-item\\n * @nosideeffects\\n */\\nDOMImplementationList.prototype.item = function(index) {};\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMImplementationSource\\n */\\nfunction DOMImplementationSource() {}\\n\\n/**\\n * @param {?string} namespaceURI\\n * @param {string} publicId\\n * @param {DocumentType} doctype\\n * @return {Document}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Level-2-Core-DOM-createDocument\\n * @nosideeffects\\n */\\nDOMImplementation.prototype.createDocument = function(namespaceURI, publicId, doctype) {};\\n\\n/**\\n * @param {string} qualifiedName\\n * @param {string} publicId\\n * @param {string} systemId\\n * @return {DocumentType}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Level-2-Core-DOM-createDocType\\n * @nosideeffects\\n */\\nDOMImplementation.prototype.createDocumentType = function(qualifiedName, publicId, systemId) {};\\n\\n/**\\n * @param {string} features\\n * @return {DOMImplementation}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-getDOMImpl\\n * @nosideeffects\\n */\\nDOMImplementationSource.prototype.getDOMImplementation = function(features) {};\\n\\n/**\\n * @param {string} features\\n * @return {DOMImplementationList}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-getDOMImpls\\n * @nosideeffects\\n */\\nDOMImplementationSource.prototype.getDOMImplementationList = function(features) {};\\n\\n/**\\n * @param {string} feature\\n * @param {string} version\\n * @return {Object}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMImplementation3-getFeature\\n * @nosideeffects\\n */\\nDOMImplementation.prototype.getFeature = function(feature, version) {};\\n\\n/**\\n * @param {Node} externalNode\\n * @return {Node}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Document3-adoptNode\\n */\\nDocument.prototype.adoptNode = function(externalNode) {};\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Document3-documentURI\\n */\\nDocument.prototype.documentURI;\\n\\n/**\\n * @type {DOMConfiguration}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Document3-domConfig\\n */\\nDocument.prototype.domConfig;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-3-C'; +a.a+="ore/core.html#Document3-inputEncoding\\n */\\nDocument.prototype.inputEncoding;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Document3-strictErrorChecking\\n */\\nDocument.prototype.strictErrorChecking;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Document3-encoding\\n */\\nDocument.prototype.xmlEncoding;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Document3-standalone\\n */\\nDocument.prototype.xmlStandalone;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Document3-version\\n */\\nDocument.prototype.xmlVersion;\\n\\n/**\\n * @return {undefined}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Document3-normalizeDocument\\n */\\nDocument.prototype.normalizeDocument = function() {};\\n\\n/**\\n * @param {Node} n\\n * @param {?string} namespaceURI\\n * @param {string} qualifiedName\\n * @return {Node}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Document3-renameNode\\n */\\nDocument.prototype.renameNode = function(n, namespaceURI, qualifiedName) {};\\n\\n/**\\n * @type {?string}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-baseURI\\n */\\nNode.prototype.baseURI;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-NodeNSLocalN\\n */\\nNode.prototype.localName;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-NodeNSname\\n */\\nNode.prototype.namespaceURI;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-NodeNSPrefix\\n */\\nNode.prototype.prefix;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-textContent\\n */\\nNode.prototype.textContent;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node-DOCUMENT_POSITION_DISCONNECTED\\n */\\nNode.DOCUMENT_POSITION_DISCONNECTED = 0x01;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node-DOCUMENT_POSITION_PRECEDING\\n */\\nNode.DOCUMENT_POSITION_PRECEDING = 0x02;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node-DOCUMENT_POSITION_FOLLOWING\\n */\\nNode.DOCUMENT_POSITION_FOLLOWING = 0x04;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node-DOCUMENT_POSITION_CONTAINS\\n */\\nNode.DOCUMENT_POSITION_CONTAINS = 0x08;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node-DOCUMENT_POSITION_CONTAINED_BY\\n */\\nNode.DOCUMENT_POSITION_CONTAINED_BY = 0x10;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node-DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC\\n */\\nNode.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20;\\n\\n/**\\n * @param {Node} other\\n * @return {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-compareDocumentPosition\\n * @nosideeffects\\n */\\nNode.prototype.compareDocumentPosition = function(other) {};\\n\\n/**\\n * @param {string} feature\\n * @param {string} version\\n * @return {Object}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-getFeature\\n * @nosideeffects\\n */\\nNode.prototype.getFeature = function(feature, version) {};\\n\\n/**\\n * @param {string} key\\n * @return {Object}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-getUserData\\n * @nosideeffects\\n */\\nNode.prototype.getUserData = function(key) {};\\n\\n/**\\n * @return {boolean}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-NodeHasAttrs\\n * @nosideeffects\\n */\\nNode.prototype.hasAttributes = function() {};\\n\\n/**\\n * @param {?string} namespaceURI\\n * @return {boolean}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-isDefaultNamespace\\n * @nosideeffects\\n */\\nNode.prototype.isDefaultNamespace = function(namespaceURI) {};\\n\\n/**\\n * @param {Node} arg\\n * @return {boolean}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-isEqualNode\\n * @nosideeffects\\n */\\nNode.prototype.isEqualNode = function(arg) {};\\n\\n/**\\n * @param {Node} other\\n * @return {boolean}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-isSameNode\\n * @nosideeffects\\n */\\nNode.prototype.isSameNode = function(other) {};\\n\\n/**\\n * @param {string} feature\\n * @param {string} version\\n * @return {boolean}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Level-2-Core-Node-supports\\n * @nosideeffects\\n */\\nNode.prototype.isSupported = function(feature, version) {};\\n\\n/**\\n * @param {string} prefix\\n * @return {string}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI\\n * @nosideeffects\\n */\\nNode.prototype.lookupNamespaceURI = function(prefix) {};\\n\\n/**\\n * @param {?string} namespaceURI\\n * @return {string}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespacePrefix\\n * @nosideeffects\\n */\\nNode.prototype.lookupPrefix = function(namespaceURI) {};\\n\\n/**\\n * @return {undefined}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-normalize\\n */\\nNode.prototype.normalize = function() {};\\n\\n/**\\n * @param {Object} key\\n * @param {Object} data\\n * @param {UserDataHandler} handler\\n * @return {Object}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-setUserData'\\n */\\nNode.prototype.setUserData = function(key, data, handler) {};\\n\\n/**\\n * @param {string} query\\n * @return {?Element}\\n * @see http://www.w3.org/TR/selectors-api/#queryselector\\n * @nosideeffects\\n */\\nNode.prototype.querySelector = function(query) {};\\n\\n/**\\n * @param {string} query\\n * @return {!NodeList}\\n * @see http://www.w3.org/TR/selectors-api/#queryselectorall\\n * @nosideeffects\\n */\\nNode.prototype.querySelectorAll = function(query) {};\\n\\n/**\\n * @type {Element}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Attr-ownerElement\\n */\\nAttr.prototype.ownerElement;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Attr-isId\\n */\\nAttr.prototype.isId;\\n\\n/**\\n * @type {TypeInfo}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Attr-schemaTypeInfo\\n */\\nAttr.prototype.schemaTypeInfo;\\n\\n/**\\n * @type {TypeInfo}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Element-schemaTypeInfo\\n */\\nElement.prototype.schemaTypeInfo;\\n\\n/**\\n * @param {?string} namespaceURI\\n * @param {string} localName\\n * @return {Attr}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-ElGetAtNodeNS\\n * @nosideeffects\\n */\\nElement.prototype.getAttributeNodeNS = function(namespaceURI, localName) {};\\n\\n/**\\n * @param {?string} namespaceURI\\n * @param {string} localName\\n * @return {string}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-ElGetAttrNS\\n * @nosideeffects\\n */\\nElement.prototype.getAttributeNS = function(namespaceURI, localName) {};\\n\\n/**\\n * @param {?string} namespaceURI\\n * @param {string} localName\\n * @return {!NodeList}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-A6C90942\\n * @nosideeffects\\n */\\nElement.prototype.getElementsByTagNameNS = function(namespaceURI, localName) {};\\n\\n/**\\n * @param {string} name\\n * @return {boolean}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-ElHasAttr\\n * @nosideeffects\\n */\\nElement.prototype.hasAttribute = function(name) {};\\n\\n/**\\n * @param {?string} namespaceURI\\n * @param {string} localName\\n * @return {boolean}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-ElHasAttrNS\\n * @nosideeffects\\n */\\nElement.prototype.hasAttributeNS = function(namespaceURI, localName) {};\\n\\n/**\\n * @param {?string} namespaceURI\\n * @param {string} localName\\n * @return {undefined}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-ElRemAtNS\\n */\\nElement.prototype.removeAttributeNS = function(namespaceURI, localName) {};\\n\\n/**\\n * @param {Attr} newAttr\\n * @return {Attr}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-ElSetAtNodeNS\\n */\\nElement.prototype.setAttributeNodeNS = function(newAttr) {};\\n\\n/**\\n * @param {?string} namespaceURI\\n * @param {string} qualifiedName\\n * @param {string|number|boolean} value Values are converted to strings with\\n * ToString, so we accept number and boolean since both convert easily to\\n * strings.\\n * @return {undefined}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-ElSetAttrNS\\n */\\nElement.prototype.setAttributeNS = function(namespaceURI, qualifiedName, value) {};\\n\\n/**\\n * @param {string} name\\n * @param {boolean} isId\\n * @return {undefined}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-ElSetIdAttr\\n */\\nElement.prototype.setIdAttribute = function(name, isId) {};\\n\\n/**\\n * @param {Attr} idAttr\\n * @param {boolean} isId\\n * @return {undefined}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-ElSetIdAttrNode\\n */\\nElement.prototype.setIdAttributeNode = function(idAttr, isId) {};\\n\\n/**\\n * @param {?string} namespaceURI\\n * @param {string} localName\\n * @param {boolean} isId\\n * @return {undefined}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-ElSetIdAttrNS\\n */\\nElement.prototype.setIdAttributeNS = function(namespaceURI, localName, isId) {};\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Text3-wholeText\\n */\\nText.prototype.wholeText;\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#TypeInfo\\n */\\nfunction TypeInfo() {}\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#TypeInfo-DERIVATION_EXTENSION\\n */\\nTypeInfo.prototype.DERIVATION_EXTENSION;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#TypeInfo-DERIVATION_LIST\\n */\\nTypeInfo.prototype.DERIVATION_LIST;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#TypeInfo-DERIVATION_RESTRICTION\\n */\\nTypeInfo.prototype.DERIVATION_RESTRICTION;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#TypeInfo-DERIVATION_UNION\\n */\\nTypeInfo.prototype.DERIVATION_UNION;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#TypeInfo-typeName\\n */\\nTypeInfo.prototype.typeName;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#TypeInfo-typeNamespace\\n */\\nTypeInfo.prototype.typeNamespace;\\n\\n/**\\n * @param {string} typeNamespaceArg\\n * @param {string} typeNameArg\\n * @param {number} derivationMethod\\n * @return {boolean}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#TypeInfo-isDerivedFrom\\n * @nosideeffects\\n */\\nTypeInfo.prototype.isDerivedFrom = function(typeNamespaceArg, typeNameArg, derivationMethod) {};\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#UserDataHandler\\n */\\nfunction UserDataHandler() {}\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#UserDataHandler-CLONED\\n */\\nUserDataHandler.prototype.NODE_CLONED = 1;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#UserDataHandler-IMPORTED\\n */\\nUserDataHandler.prototype.NODE_IMPORTED = 2;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#UserDataHandler-DELETED\\n */\\nUserDataHandler.prototype.NODE_DELETED = 3;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#UserDataHandler-RENAMED\\n */\\nUserDataHandler.prototype.NODE_RENAMED = 4;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#UserDataHandler-ADOPTED\\n */\\nUserDataHandler.prototype.NODE_ADOPTED = 5;\\n\\n/**\\n * @param {number} operation\\n * @param {string} key\\n * @param {*=} opt_data\\n * @param {?Node=} opt_src\\n * @param {?Node=} opt_dst\\n * @return {undefined}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-handleUserDataEvent\\n */\\nUserDataHandler.prototype.handle = function(operation, key, opt_data,\\n opt_src, opt_dst) {};\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ERROR-Interfaces-DOMError\\n */\\nfunction DOMError() {}\\n\\n/**\\n * @type {DOMLocator}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ERROR-DOMError-location\\n */\\nDOMError.prototype.location;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ERROR-DOMError-message\\n */\\nDOMError.prototype.message;\\n\\n/**\\n * @type {Object}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ERROR-DOMError-relatedData\\n */\\nDOMError.prototype.relatedData;\\n\\n/**\\n * @type {Object}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ERROR-DOMError-relatedException\\n */\\nDOMError.prototype.relatedException;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ERROR-DOMError-severity-warning\\n */\\nDOMError.SEVERITY_WARNING = 1;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ERROR-DOMError-severity-error\\n */\\nDOMError.SEVERITY_ERROR = 2;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ERROR-DOMError-severity-fatal-error\\n */\\nDOMError.SEVERITY_FATAL_ERROR = 3;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ERROR-DOMError-severity\\n */\\nDOMError.prototype.severity;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ERROR-DOMError-type\\n */\\nDOMError.prototype.type;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/dom/#domerror\\n */\\nDOMError.prototype.name;\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ERROR-Interfaces-DOMErrorHandler\\n */\\nfunction DOMErrorHandler() {}\\n\\n/**\\n * @param {DOMError} error\\n * @return {boolean}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-ERRORS-DOMErrorHandler-handleError\\n */\\nDOMErrorHandler.prototype.handleError = function(error) {};\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Interfaces-DOMLocator\\n */\\nfunction DOMLocator() {}\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMLocator-byteOffset\\n */\\nDOMLocator.prototype.byteOffset;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMLocator-column-number\\n */\\nDOMLocator.prototype.columnNumber;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMLocator-line-number\\n */\\nDOMLocator.prototype.lineNumber;\\n\\n/**\\n * @type {Node}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMLocator-node\\n */\\nDOMLocator.prototype.relatedNode;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMLocator-uri\\n */\\nDOMLocator.prototype.uri;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMLocator-utf16Offset\\n */\\nDOMLocator.prototype.utf16Offset;\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMConfiguration\\n */\\nfunction DOMConfiguration() {}\\n\\n/**\\n * @type {DOMStringList}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMConfiguration-parameterNames\\n */\\nDOMConfiguration.prototype.parameterNames;\\n\\n/**\\n * @param {string} name\\n * @return {boolean}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMConfiguration-canSetParameter\\n * @nosideeffects\\n */\\nDOMConfiguration.prototype.canSetParameter = function(name) {};\\n\\n/**\\n * @param {string} name\\n * @return {*}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMConfiguration-getParameter\\n * @nosideeffects\\n */\\nDOMConfiguration.prototype.getParameter = function(name) {};\\n\\n/**\\n * @param {string} name\\n * @param {*} value\\n * @return {*}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMConfiguration-property\\n */\\nDOMConfiguration.prototype.setParameter = function(name, value) {};\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-Core-DocType-internalSubset\\n */\\nDocumentType.prototype.internalSubset;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-Core-DocType-publicId\\n */\\nDocumentType.prototype.publicId;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-Core-DocType-systemId\\n */\\nDocumentType.prototype.systemId;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.or"; +a.a+='g/TR/DOM-Level-3-Core/core.html#Entity3-inputEncoding\\n */\\nEntity.prototype.inputEncoding;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Entity3-encoding\\n */\\nEntity.prototype.xmlEncoding;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Entity3-version\\n */\\nEntity.prototype.xmlVersion;\\n","externs/w3c_dom4.js":"/*\\n * Copyright 2016 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for W3C\'s DOM4 specification. This file depends on\\n * w3c_dom3.js. The whole file has been fully type annotated. Created from\\n * https://www.w3.org/TR/domcore/.\\n *\\n * @externs\\n * @author zhoumotongxue008@gmail.com (Michael Zhou)\\n */\\n\\n/**\\n * @typedef {?(DocumentType|Element|CharacterData)}\\n * @see https://www.w3.org/TR/domcore/#interface-childnode\\n */\\nvar ChildNode;\\n\\n/**\\n * @return {undefined}\\n * @see https://www.w3.org/TR/domcore/#dom-childnode-remove\\n */\\nDocumentType.prototype.remove = function() {};\\n\\n/**\\n * @return {undefined}\\n * @see https://www.w3.org/TR/domcore/#dom-childnode-remove\\n */\\nElement.prototype.remove = function() {};\\n\\n/**\\n * @return {undefined}\\n * @see https://www.w3.org/TR/domcore/#dom-childnode-remove\\n */\\nCharacterData.prototype.remove = function() {};\\n\\n/**\\n * @param {...(!Node|string)} nodes\\n * @return {undefined}\\n * @see https://dom.spec.whatwg.org/#dom-childnode-replacewith\\n */\\nDocumentType.prototype.replaceWith = function(nodes) {};\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/2015/REC-dom-20151119/#dfn-error-names-table\\n */\\nDOMException.SECURITY_ERR = 18;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/2015/REC-dom-20151119/#dfn-error-names-table\\n */\\nDOMException.NETWORK_ERR = 19;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/2015/REC-dom-20151119/#dfn-error-names-table\\n */\\nDOMException.ABORT_ERR = 20;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/2015/REC-dom-20151119/#dfn-error-names-table\\n */\\nDOMException.URL_MISMATCH_ERR = 21;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/2015/REC-dom-20151119/#dfn-error-names-table\\n */\\nDOMException.QUOTA_EXCEEDED_ERR = 22;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/2015/REC-dom-20151119/#dfn-error-names-table\\n */\\nDOMException.TIMEOUT_ERR = 23;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/2015/REC-dom-20151119/#dfn-error-names-table\\n */\\nDOMException.INVALID_NODE_TYPE_ERR = 24;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/2015/REC-dom-20151119/#dfn-error-names-table\\n */\\nDOMException.DATA_CLONE_ERR = 25;\\n\\n/**\\n * @param {...(!Node|string)} nodes\\n * @return {undefined}\\n * @see https://dom.spec.whatwg.org/#dom-childnode-replacewith\\n */\\nElement.prototype.replaceWith = function(nodes) {};\\n\\n/**\\n * @param {...(!Node|string)} nodes\\n * @return {undefined}\\n * @see https://dom.spec.whatwg.org/#dom-childnode-replacewith\\n */\\nCharacterData.prototype.replaceWith = function(nodes) {};\\n\\n/**\\n * @return {!Array}\\n * @see https://dom.spec.whatwg.org/#dom-element-getattributenames\\n */\\nElement.prototype.getAttributeNames = function() {};\\n\\n/**\\n * @param {...(!Node|string)} nodes\\n * @return {undefined}\\n * @see https://dom.spec.whatwg.org/#dom-parentnode-append\\n */\\nElement.prototype.append = function(nodes) {};\\n\\n/**\\n * @param {...(!Node|string)} nodes\\n * @return {undefined}\\n * @see https://dom.spec.whatwg.org/#dom-parentnode-append\\n */\\nDocument.prototype.append = function(nodes) {};\\n\\n/**\\n * @param {...(!Node|string)} nodes\\n * @return {undefined}\\n * @see https://dom.spec.whatwg.org/#dom-parentnode-append\\n */\\nDocumentFragment.prototype.append = function(nodes) {};\\n\\n/**\\n * @param {...(!Node|string)} nodes\\n * @return {undefined}\\n * @see https://dom.spec.whatwg.org/#dom-parentnode-prepend\\n */\\nElement.prototype.prepend = function(nodes) {};\\n\\n/**\\n * @param {...(!Node|string)} nodes\\n * @return {undefined}\\n * @see https://dom.spec.whatwg.org/#dom-parentnode-prepend\\n */\\nDocument.prototype.prepend = function(nodes) {};\\n\\n/**\\n * @param {...(!Node|string)} nodes\\n * @return {undefined}\\n * @see https://dom.spec.whatwg.org/#dom-parentnode-prepend\\n */\\nDocumentFragment.prototype.prepend = function(nodes) {};\\n\\n/**\\n * @param {...(!Node|string)} nodes\\n * @return {undefined}\\n * @see https://dom.spec.whatwg.org/#dom-childnode-before\\n */\\nElement.prototype.before = function(nodes) {};\\n\\n/**\\n * @param {...(!Node|string)} nodes\\n * @return {undefined}\\n * @see https://dom.spec.whatwg.org/#dom-childnode-before\\n */\\nDocumentType.prototype.before = function(nodes) {};\\n\\n/**\\n * @param {...(!Node|string)} nodes\\n * @return {undefined}\\n * @see https://dom.spec.whatwg.org/#dom-childnode-before\\n */\\nCharacterData.prototype.before = function(nodes) {};\\n\\n/**\\n * @param {...(!Node|string)} nodes\\n * @return {undefined}\\n * @see https://dom.spec.whatwg.org/#dom-childnode-after\\n */\\nElement.prototype.after = function(nodes) {};\\n\\n/**\\n * @param {...(!Node|string)} nodes\\n * @return {undefined}\\n * @see https://dom.spec.whatwg.org/#dom-childnode-after\\n */\\nDocumentType.prototype.after = function(nodes) {};\\n\\n/**\\n * @param {...(!Node|string)} nodes\\n * @return {undefined}\\n * @see https://dom.spec.whatwg.org/#dom-childnode-after\\n */\\nCharacterData.prototype.after = function(nodes) {};\\n\\n/**\\n * @param {string} name\\n * @param {boolean=} force\\n * @return {boolean}\\n * @throws {DOMException}\\n * @see https://dom.spec.whatwg.org/#dom-element-toggleattribute\\n */\\nElement.prototype.toggleAttribute = function(name, force) {};\\n","externs/gecko_dom.js":"/*\\n * Copyright 2008 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for all the extensions over\\n * W3C\'s DOM specification by Gecko. This file depends on\\n * w3c_dom2.js.\\n *\\n * When a non-standard extension appears in both Gecko and IE, we put\\n * it in gecko_dom.js\\n *\\n * @externs\\n * @author acleung@google.com (Alan Leung)\\n */\\n\\n// TODO: Almost all of it has not been annotated with types.\\n\\n// Gecko DOM;\\n\\n/**\\n * Mozilla only???\\n * @constructor\\n * @extends {HTMLElement}\\n */\\nfunction HTMLSpanElement() {}\\n\\n/**\\n * @see https://developer.mozilla.org/en/Components_object\\n */\\nWindow.prototype.Components;\\n\\n/**\\n * @type {Window}\\n * @see https://developer.mozilla.org/en/DOM/window.content\\n */\\nWindow.prototype.content;\\n\\n/**\\n * @type {boolean}\\n * @see https://developer.mozilla.org/en/DOM/window.closed\\n */\\nWindow.prototype.closed;\\n\\n/** @see https://developer.mozilla.org/en/DOM/window.controllers */\\nWindow.prototype.controllers;\\n\\n/** @see https://developer.mozilla.org/en/DOM/window.crypto */\\nWindow.prototype.crypto;\\n\\n/**\\n * Gets/sets the status bar text for the given window.\\n * @type {string}\\n * @see https://developer.mozilla.org/en/DOM/window.defaultStatus\\n */\\nWindow.prototype.defaultStatus;\\n\\n/** @see https://developer.mozilla.org/en/DOM/window.dialogArguments */\\nWindow.prototype.dialogArguments;\\n\\n/** @see https://developer.mozilla.org/en/DOM/window.directories */\\nWindow.prototype.directories;\\n\\n/**\\n * @type {HTMLObjectElement|HTMLIFrameElement|null}\\n * @see https://developer.mozilla.org/en/DOM/window.frameElement\\n */\\nWindow.prototype.frameElement;\\n\\n/**\\n * Allows lookup of frames by index or by name.\\n * @type {!Window}\\n * @see https://developer.mozilla.org/en/DOM/window.frames\\n */\\nWindow.prototype.frames;\\n\\n/**\\n * @type {boolean}\\n * @see https://developer.mozilla.org/en/DOM/window.fullScreen\\n */\\nWindow.prototype.fullScreen;\\n\\n/**\\n * @return {!Promise}\\n * @see http://www.w3.org/TR/battery-status/\\n */\\nNavigator.prototype.getBattery = function() {};\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/Storage#globalStorage\\n */\\nWindow.prototype.globalStorage;\\n\\n/**\\n * @type {!History}\\n * @suppress {duplicate}\\n * @see https://developer.mozilla.org/en/DOM/window.history\\n */\\nvar history;\\n\\n/**\\n * Returns the number of frames (either frame or iframe elements) in the\\n * window.\\n *\\n * @type {number}\\n * @see https://developer.mozilla.org/en/DOM/window.length\\n */\\nWindow.prototype.length;\\n\\n/**\\n * Location has an exception in the DeclaredGlobalExternsOnWindow pass\\n * so we have to manually include it:\\n * https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/DeclaredGlobalExternsOnWindow.java#L116\\n *\\n * @type {!Location}\\n * @implicitCast\\n * @see https://developer.mozilla.org/en/DOM/window.location\\n */\\nWindow.prototype.location;\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/window.locationbar\\n */\\nWindow.prototype.locationbar;\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/window.menubar\\n */\\nWindow.prototype.menubar;\\n\\n/**\\n * @type {string}\\n * @see https://developer.mozilla.org/en/DOM/window.name\\n */\\nWindow.prototype.name;\\n\\n/**\\n * @type {Navigator}\\n * @see https://developer.mozilla.org/en/DOM/window.navigator\\n */\\nWindow.prototype.navigator;\\n\\n/**\\n * @type {?Window}\\n * @see https://developer.mozilla.org/en/DOM/window.opener\\n */\\nWindow.prototype.opener;\\n\\n/**\\n * @type {!Window}\\n * @see https://developer.mozilla.org/en/DOM/window.parent\\n */\\nWindow.prototype.parent;\\n\\n/** @see https://developer.mozilla.org/en/DOM/window.personalbar */\\nWindow.prototype.personalbar;\\n\\n/** @see https://developer.mozilla.org/en/DOM/window.pkcs11 */\\nWindow.prototype.pkcs11;\\n\\n/** @see https://developer.mozilla.org/en/DOM/window */\\nWindow.prototype.returnValue;\\n\\n/** @see https://developer.mozilla.org/en/DOM/window.scrollbars */\\nWindow.prototype.scrollbars;\\n\\n/**\\n * @type {number}\\n * @see https://developer.mozilla.org/En/DOM/window.scrollMaxX\\n */\\nWindow.prototype.scrollMaxX;\\n\\n/**\\n * @type {number}\\n * @see https://developer.mozilla.org/En/DOM/window.scrollMaxY\\n */\\nWindow.prototype.scrollMaxY;\\n\\n/**\\n * @type {!Window}\\n * @see https://developer.mozilla.org/en/DOM/window.self\\n */\\nWindow.prototype.self;\\n\\n/** @see https://developer.mozilla.org/en/DOM/Storage#sessionStorage */\\nWindow.prototype.sessionStorage;\\n\\n/** @see https://developer.mozilla.org/en/DOM/window.sidebar */\\nWindow.prototype.sidebar;\\n\\n/**\\n * @type {?string}\\n * @see https://developer.mozilla.org/en/DOM/window.status\\n */\\nWindow.prototype.status;\\n\\n/** @see https://developer.mozilla.org/en/DOM/window.statusbar */\\nWindow.prototype.statusbar;\\n\\n/** @see https://developer.mozilla.org/en/DOM/window.toolbar */\\nWindow.prototype.toolbar;\\n\\n/**\\n * @type {!Window}\\n * @see https://developer.mozilla.org/en/DOM/window.self\\n */\\nWindow.prototype.top;\\n\\n/**\\n * @type {!Window}\\n * @see https://developer.mozilla.org/en/DOM/window.self\\n */\\nWindow.prototype.window;\\n\\n/**\\n * @param {*} message\\n * @see https://developer.mozilla.org/en/DOM/window.alert\\n * @return {undefined}\\n */\\nWindow.prototype.alert = function(message) {};\\n\\n/**\\n * Decodes a string of data which has been encoded using base-64 encoding.\\n *\\n * @param {string} encodedData\\n * @return {string}\\n * @see https://developer.mozilla.org/en/DOM/window.atob\\n * @nosideeffects\\n */\\nfunction atob(encodedData) {}\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/window.back\\n * @return {undefined}\\n */\\nWindow.prototype.back = function() {};\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/window.blur\\n * @return {undefined}\\n */\\nWindow.prototype.blur = function() {};\\n\\n/**\\n * @param {string} stringToEncode\\n * @return {string}\\n * @see https://developer.mozilla.org/en/DOM/window.btoa\\n * @nosideeffects\\n */\\nfunction btoa(stringToEncode) {}\\n\\n/** @deprecated */\\nWindow.prototype.captureEvents;\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/window.close\\n * @return {undefined}\\n */\\nWindow.prototype.close = function() {};\\n\\n/**@see https://developer.mozilla.org/en/DOM/window.find */\\nWindow.prototype.find;\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/window.focus\\n * @return {undefined}\\n */\\nWindow.prototype.focus = function() {};\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/window.forward\\n * @return {undefined}\\n */\\nWindow.prototype.forward = function() {};\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/window.getAttention\\n * @return {undefined}\\n */\\nWindow.prototype.getAttention = function() {};\\n\\n/**\\n * @return {Selection}\\n * @see https://developer.mozilla.org/en/DOM/window.getSelection\\n * @nosideeffects\\n */\\nWindow.prototype.getSelection = function() {};\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/window.home\\n * @return {undefined}\\n */\\nWindow.prototype.home = function() {};\\n\\nWindow.prototype.openDialog;\\nWindow.prototype.releaseEvents;\\nWindow.prototype.scrollByLines;\\nWindow.prototype.scrollByPages;\\n\\n/**\\n * @param {string} uri\\n * @param {?=} opt_arguments\\n * @param {string=} opt_options\\n * @see https://developer.mozilla.org/en/DOM/window.showModalDialog\\n */\\nWindow.prototype.showModalDialog;\\n\\nWindow.prototype.sizeToContent;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536769(VS.85).aspx\\n * @return {undefined}\\n */\\nWindow.prototype.stop = function() {};\\n\\nWindow.prototype.updateCommands;\\n\\n// properties of Document\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/document.alinkColor\\n * @type {string}\\n */\\nDocument.prototype.alinkColor;\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/document.anchors\\n * @type {HTMLCollection}\\n */\\nDocument.prototype.anchors;\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/document.applets\\n * @type {HTMLCollection}\\n */\\nDocument.prototype.applets;\\n/** @type {boolean} */ Document.prototype.async;\\n/** @type {string?} */ Document.prototype.baseURI;\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/document.bgColor\\n * @type {string}\\n */\\nDocument.prototype.bgColor;\\n\\n/** @type {HTMLBodyElement} */ Document.prototype.body;\\nDocument.prototype.characterSet;\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/document.compatMode\\n * @type {string}\\n */\\nDocument.prototype.compatMode;\\n\\nDocument.prototype.contentType;\\n/** @type {string} */ Document.prototype.cookie;\\n\\n/**\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/defaultView\\n * @type {?Window}\\n */\\nDocument.prototype.defaultView;\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/document.designMode\\n * @type {string}\\n */\\nDocument.prototype.designMode;\\n\\nDocument.prototype.documentURIObject;\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/document.domain\\n * @type {string}\\n */\\nDocument.prototype.domain;\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/document.embeds\\n * @type {HTMLCollection}\\n */\\nDocument.prototype.embeds;\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/document.fgColor\\n * @type {string}\\n */\\nDocument.prototype.fgColor;\\n\\n/** @type {Element} */ Document.prototype.firstChild;\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/document.forms\\n * @type {HTMLCollection}\\n */\\nDocument.prototype.forms;\\n\\n/** @type {number} */\\nDocument.prototype.height;\\n\\n/** @type {HTMLCollection} */\\nDocument.prototype.images;\\n\\n/**\\n * @type {string}\\'; +a.a+='n * @see https://developer.mozilla.org/en/DOM/document.lastModified\\n */\\nDocument.prototype.lastModified;\\n\\n/**\\n * @type {string}\\n * @see https://developer.mozilla.org/en/DOM/document.linkColor\\n */\\nDocument.prototype.linkColor;\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/document.links\\n * @type {HTMLCollection<(!HTMLAreaElement|!HTMLAnchorElement)>}\\n */\\nDocument.prototype.links;\\n\\n/**\\n * @type {!Location}\\n * @implicitCast\\n */\\nDocument.prototype.location;\\n\\nDocument.prototype.namespaceURI;\\nDocument.prototype.nodePrincipal;\\nDocument.prototype.plugins;\\nDocument.prototype.popupNode;\\n\\n/**\\n * @type {string}\\n * @see https://developer.mozilla.org/en/DOM/document.referrer\\n */\\nDocument.prototype.referrer;\\n\\n/**\\n * @type {StyleSheetList}\\n * @see https://developer.mozilla.org/en/DOM/document.styleSheets\\n */\\nDocument.prototype.styleSheets;\\n\\n/** @type {?string} */ Document.prototype.title;\\nDocument.prototype.tooltipNode;\\n/** @type {string} */ Document.prototype.URL;\\n\\n/**\\n * @type {string}\\n * @see https://developer.mozilla.org/en/DOM/document.vlinkColor\\n */\\nDocument.prototype.vlinkColor;\\n\\n/** @type {number} */ Document.prototype.width;\\n\\n// Methods of Document\\n/**\\n * @see https://developer.mozilla.org/en/DOM/document.clear\\n * @return {undefined}\\n */\\nDocument.prototype.clear = function() {};\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/document.close\\n */\\nDocument.prototype.close;\\n\\n/**\\n * @param {string} type\\n * @return {Event}\\n */\\nDocument.prototype.createEvent = function(type) {};\\nDocument.prototype.createNSResolver;\\n/** @return {Range} */ Document.prototype.createRange = function() {};\\nDocument.prototype.createTreeWalker;\\n\\nDocument.prototype.evaluate;\\n\\n/**\\n * @param {string} commandName\\n * @param {?boolean=} opt_showUi\\n * @param {*=} opt_value\\n * @see https://developer.mozilla.org/en/Rich-Text_Editing_in_Mozilla#Executing_Commands\\n */\\nDocument.prototype.execCommand;\\n\\n/**\\n * @param {string} name\\n * @return {!NodeList}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en/DOM/document.getElementsByClassName\\n */\\nDocument.prototype.getElementsByClassName = function(name) {};\\n\\n/**\\n * @param {string} uri\\n * @return {undefined}\\n */\\nDocument.prototype.load = function(uri) {};\\nDocument.prototype.loadOverlay;\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/document.open\\n */\\nDocument.prototype.open;\\n\\n/**\\n * @see https://developer.mozilla.org/en/Midas\\n * @see http://msdn.microsoft.com/en-us/library/ms536676(VS.85).aspx\\n */\\nDocument.prototype.queryCommandEnabled;\\n\\n/**\\n * @see https://developer.mozilla.org/en/Midas\\n * @see http://msdn.microsoft.com/en-us/library/ms536678(VS.85).aspx\\n */\\nDocument.prototype.queryCommandIndeterm;\\n\\n/**\\n * @see https://developer.mozilla.org/en/Midas\\n * @see http://msdn.microsoft.com/en-us/library/ms536679(VS.85).aspx\\n */\\nDocument.prototype.queryCommandState;\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/document.queryCommandSupported\\n * @see http://msdn.microsoft.com/en-us/library/ms536681(VS.85).aspx\\n * @param {string} command\\n * @return {?} Implementation-specific.\\n */\\nDocument.prototype.queryCommandSupported;\\n\\n/**\\n * @see https://developer.mozilla.org/en/Midas\\n * @see http://msdn.microsoft.com/en-us/library/ms536683(VS.85).aspx\\n */\\nDocument.prototype.queryCommandValue;\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/document.write\\n * @param {!TrustedHTML|string} text\\n * @return {undefined}\\n */\\nDocument.prototype.write = function(text) {};\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/document.writeln\\n * @param {!TrustedHTML|string} text\\n * @return {undefined}\\n */\\nDocument.prototype.writeln = function(text) {};\\n\\nDocument.prototype.ononline;\\nDocument.prototype.onoffline;\\n\\n// XUL\\n/**\\n * @see http://developer.mozilla.org/en/DOM/document.getBoxObjectFor\\n * @return {BoxObject}\\n * @nosideeffects\\n */\\nDocument.prototype.getBoxObjectFor = function(element) {};\\n\\n// From:\\n// http://lxr.mozilla.org/mozilla1.8/source/dom/public/idl/range/nsIDOMNSRange.idl\\n\\n/**\\n * @param {!TrustedHTML|string} tag\\n * @return {DocumentFragment}\\n */\\nRange.prototype.createContextualFragment;\\n\\n/**\\n * @param {Node} parent\\n * @param {number} offset\\n * @return {boolean}\\n * @nosideeffects\\n */\\nRange.prototype.isPointInRange;\\n\\n/**\\n * @param {Node} parent\\n * @param {number} offset\\n * @return {number}\\n * @nosideeffects\\n */\\nRange.prototype.comparePoint;\\n\\n/**\\n * @param {Node} n\\n * @return {boolean}\\n * @nosideeffects\\n */\\nRange.prototype.intersectsNode;\\n\\n/**\\n * @param {Node} n\\n * @return {number}\\n * @nosideeffects\\n */\\nRange.prototype.compareNode;\\n\\n\\n/** @constructor */\\nfunction Selection() {}\\n\\n/**\\n * @type {Node}\\n * @see https://developer.mozilla.org/en/DOM/Selection/anchorNode\\n */\\nSelection.prototype.anchorNode;\\n\\n/**\\n * @type {number}\\n * @see https://developer.mozilla.org/en/DOM/Selection/anchorOffset\\n */\\nSelection.prototype.anchorOffset;\\n\\n/**\\n * @type {Node}\\n * @see https://developer.mozilla.org/en/DOM/Selection/focusNode\\n */\\nSelection.prototype.focusNode;\\n\\n/**\\n * @type {number}\\n * @see https://developer.mozilla.org/en/DOM/Selection/focusOffset\\n */\\nSelection.prototype.focusOffset;\\n\\n/**\\n * @type {boolean}\\n * @see https://developer.mozilla.org/en/DOM/Selection/isCollapsed\\n */\\nSelection.prototype.isCollapsed;\\n\\n/**\\n * @type {number}\\n * @see https://developer.mozilla.org/en/DOM/Selection/rangeCount\\n */\\nSelection.prototype.rangeCount;\\n\\n/**\\n * @param {Range} range\\n * @return {undefined}\\n * @see https://developer.mozilla.org/en/DOM/Selection/addRange\\n */\\nSelection.prototype.addRange = function(range) {};\\n\\n/**\\n * @param {number} index\\n * @return {Range}\\n * @see https://developer.mozilla.org/en/DOM/Selection/getRangeAt\\n * @nosideeffects\\n */\\nSelection.prototype.getRangeAt = function(index) {};\\n\\n/**\\n * @param {Node} node\\n * @param {number} index\\n * @return {undefined}\\n * @see https://developer.mozilla.org/en/DOM/Selection/collapse\\n */\\nSelection.prototype.collapse = function(node, index) {};\\n\\n/**\\n * @return {undefined}\\n * @see https://developer.mozilla.org/en/DOM/Selection/collapseToEnd\\n */\\nSelection.prototype.collapseToEnd = function() {};\\n\\n/**\\n * @return {undefined}\\n * @see https://developer.mozilla.org/en/DOM/Selection/collapseToStart\\n */\\nSelection.prototype.collapseToStart = function() {};\\n\\n/**\\n * @param {Node} node\\n * @param {boolean} partlyContained\\n * @return {boolean}\\n * @see https://developer.mozilla.org/en/DOM/Selection/containsNode\\n * @nosideeffects\\n */\\nSelection.prototype.containsNode = function(node, partlyContained) {};\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/Selection/deleteFromDocument\\n * @return {undefined}\\n */\\nSelection.prototype.deleteFromDocument = function() {};\\n\\n/**\\n * @param {Node} parentNode\\n * @param {number} offset\\n * @see https://developer.mozilla.org/en/DOM/Selection/extend\\n * @return {undefined}\\n */\\nSelection.prototype.extend = function(parentNode, offset) {};\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/Selection/removeAllRanges\\n * @return {undefined}\\n */\\nSelection.prototype.removeAllRanges = function() {};\\n\\n/**\\n * @param {Range} range\\n * @see https://developer.mozilla.org/en/DOM/Selection/removeRange\\n * @return {undefined}\\n */\\nSelection.prototype.removeRange = function(range) {};\\n\\n/**\\n * @param {Node} parentNode\\n * @see https://developer.mozilla.org/en/DOM/Selection/selectAllChildren\\n */\\nSelection.prototype.selectAllChildren;\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/Selection/selectionLanguageChange\\n */\\nSelection.prototype.selectionLanguageChange;\\n\\n/**\\n * @type {!NodeList}\\n * @see https://developer.mozilla.org/en/DOM/element.children\\n */\\nElement.prototype.children;\\n\\n/**\\n * Firebug sets this property on elements it is inserting into the DOM.\\n * @type {boolean}\\n */\\nElement.prototype.firebugIgnore;\\n\\n/**\\n * Note: According to the spec, id is actually defined on HTMLElement and\\n * SVGElement, rather than Element. Deliberately ignore this so that saying\\n * Element.id is allowed.\\n * @type {string}\\n * @implicitCast\\n */\\nElement.prototype.id;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Parsing/#widl-Element-innerHTML\\n * @implicitCast\\n */\\nElement.prototype.innerHTML;\\n\\n/**\\n * Note: According to the spec, name is defined on specific types of\\n * HTMLElements, rather than on Node, Element, or HTMLElement directly.\\n * Ignore this.\\n * @type {string}\\n */\\nElement.prototype.name;\\n\\nElement.prototype.nodePrincipal;\\n\\n/**\\n * @type {!CSSStyleDeclaration}\\n * This belongs on HTMLElement rather than Element, but that\\n * breaks a lot.\\n * TODO(rdcronin): Remove this declaration once the breakage is fixed.\\n */\\nElement.prototype.style;\\n\\n/**\\n * @override\\n * @return {!Element}\\n */\\nElement.prototype.cloneNode = function(deep) {};\\n\\n/** @return {undefined} */\\nElement.prototype.blur = function() {};\\n\\n/** @return {undefined} */\\nElement.prototype.click = function() {};\\n\\n/**\\n * @param {{preventScroll: boolean}=} focusOption\\n * @return {undefined}\\n * @see https://html.spec.whatwg.org/multipage/interaction.html#focus-management-apis\\n */\\nElement.prototype.focus = function(focusOption) {};\\n\\n/** @type {number} */\\nHTMLInputElement.prototype.selectionStart;\\n\\n/** @type {number} */\\nHTMLInputElement.prototype.selectionEnd;\\n\\n/**\\n * @param {number} selectionStart\\n * @param {number} selectionEnd\\n * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/editing.html#dom-textarea/input-setselectionrange\\n * @return {undefined}\\n */\\nHTMLInputElement.prototype.setSelectionRange =\\n function(selectionStart, selectionEnd) {};\\n\\n/** @type {number} */\\nHTMLTextAreaElement.prototype.selectionStart;\\n\\n/** @type {number} */\\nHTMLTextAreaElement.prototype.selectionEnd;\\n\\n/**\\n * @param {number} selectionStart\\n * @param {number} selectionEnd\\n * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/editing.html#dom-textarea/input-setselectionrange\\n * @return {undefined}\\n */\\nHTMLTextAreaElement.prototype.setSelectionRange =\\n function(selectionStart, selectionEnd) {};\\n\\n/**\\n * @type {string}\\n * @see https://developer.mozilla.org/en/Navigator.buildID\\n */\\nNavigator.prototype.buildID;\\n\\n/**\\n * @type {!Array|undefined}\\n * @see https://developer.mozilla.org/en/Navigator.languages\\n */\\nNavigator.prototype.languages;\\n\\n/**\\n * @type {string}\\n * @see https://developer.mozilla.org/en/Navigator.oscpu\\n */\\nNavigator.prototype.oscpu;\\n\\n/**\\n * @type {string}\\n * @see https://developer.mozilla.org/en/Navigator.productSub\\n */\\nNavigator.prototype.productSub;\\n\\n/**\\n * @type {string}\\n * @see https://developer.mozilla.org/en/Navigator.securityPolicy\\n */\\nNavigator.prototype.securityPolicy;\\n\\n/**\\n * @param {string} url\\n * @param {ArrayBufferView|Blob|string|FormData=} opt_data\\n * @return {boolean}\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/navigator.sendBeacon\\n */\\nNavigator.prototype.sendBeacon = function(url, opt_data) {};\\n\\n/**\\n * @type {string}\\n * @see https://developer.mozilla.org/en/Navigator.vendor\\n */\\nNavigator.prototype.vendor;\\n\\n/**\\n * @type {string}\\n * @see https://developer.mozilla.org/en/Navigator.vendorSub\\n */\\nNavigator.prototype.vendorSub;\\n\\n\\n/** @constructor */\\nfunction BoxObject() {}\\n\\n/** @type {Element} */\\nBoxObject.prototype.element;\\n\\n/** @type {number} */\\nBoxObject.prototype.screenX;\\n\\n/** @type {number} */\\nBoxObject.prototype.screenY;\\n\\n/** @type {number} */\\nBoxObject.prototype.x;\\n\\n/** @type {number} */\\nBoxObject.prototype.y;\\n\\n/** @type {number} */\\nBoxObject.prototype.width;\\n\\n\\n/**\\n * @param {Element} element\\n * @param {?string=} pseudoElt\\n * @return {?CSSStyleDeclaration}\\n * @nosideeffects\\n * @see https://bugzilla.mozilla.org/show_bug.cgi?id=548397\\n */\\nfunction getComputedStyle(element, pseudoElt) {}\\n","externs/ie_dom.js":"/*\\n * Copyright 2008 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for all the extensions over the\\n * W3C\'s DOM specification by IE in JScript. This file depends on\\n * w3c_dom2.js. The whole file has NOT been fully type annotated.\\n *\\n * When a non-standard extension appears in both Gecko and IE, we put\\n * it in gecko_dom.js\\n *\\n * @externs\\n * @author acleung@google.com (Alan Leung)\\n * @author stevey@google.com (Steve Yegge)\\n */\\n\\n// TODO(nicksantos): Rewrite all the DOM interfaces as interfaces, instead\\n// of kludging them as an inheritance hierarchy.\\n\\n/**\\n * @constructor\\n * @extends {Document}\\n * @see http://msdn.microsoft.com/en-us/library/ms757878(VS.85).aspx\\n */\\nfunction XMLDOMDocument() {}\\n\\n/**\\n * @type {boolean}\\n * @see http://msdn.microsoft.com/en-us/library/ms761398(VS.85).aspx\\n */\\nXMLDOMDocument.prototype.async;\\n\\n/**\\n * @type {!Function}\\n * @see http://msdn.microsoft.com/en-us/library/ms762647(VS.85).aspx\\n */\\nXMLDOMDocument.prototype.ondataavailable;\\n\\n/**\\n * @type {!Function}\\n * @see http://msdn.microsoft.com/en-us/library/ms764640(VS.85).aspx\\n */\\nXMLDOMDocument.prototype.onreadystatechange;\\n\\n/**\\n * @type {!Function}\\n * @see http://msdn.microsoft.com/en-us/library/ms753795(VS.85).aspx\\n */\\nXMLDOMDocument.prototype.ontransformnode;\\n\\n/**\\n * @type {Object}\\n * @see http://msdn.microsoft.com/en-us/library/ms756041(VS.85).aspx\\n */\\nXMLDOMDocument.prototype.parseError;\\n\\n/**\\n * @type {boolean}\\n * @see http://msdn.microsoft.com/en-us/library/ms761353(VS.85).aspx\\n */\\nXMLDOMDocument.prototype.preserveWhiteSpace;\\n\\n/**\\n * @type {number}\\n * @see http://msdn.microsoft.com/en-us/library/ms753702(VS.85).aspx\\n */\\nXMLDOMDocument.prototype.readyState;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms762283(VS.85).aspx\\n * @type {boolean}\\n */\\nXMLDOMDocument.prototype.resolveExternals;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms760290(v=vs.85).aspx\\n * @param {string} name\\n * @param {*} value\\n * @return {undefined}\\n */\\nXMLDOMDocument.prototype.setProperty = function(name, value) {};\\n\\n/**\\n * @type {string}\\n * @see http://msdn.microsoft.com/en-us/library/ms767669(VS.85).aspx\\n */\\nXMLDOMDocument.prototype.url;\\n\\n/**\\n * @type {boolean}\\n * @see http://msdn.microsoft.com/en-us/library/ms762791(VS.85).aspx\\n */\\nXMLDOMDocument.prototype.validateOnParse;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms763830(VS.85).aspx\\n * @return {undefined}\\n */\\nXMLDOMDocument.prototype.abort = function() {};\\n\\n/**\\n * @param {*} type\\n * @param {string} name\\n * @param {string} namespaceURI\\n * @return {Node}\\n * @see http://msdn.microsoft.com/en-us/library/ms757901(VS.85).aspx\\n * @nosideeffects\\n */\\nXMLDOMDocument.prototype.createNode = function(type, name, namespaceURI) {};\\n\\n/**\\n * @param {string} xmlSource\\n * @return {undefined}\\n * @see http://msdn.microsoft.com/en-us/library/ms762722(VS.85).aspx\\n * @override\\n */\\nXMLDOMDocument.prototype.load = function(xmlSource) {};\\n\\n/**\\n * @param {string} xmlString\\n * @return {boolean}\\n * @see http://msdn.microsoft.com/en-us/library/ms754585(VS.85).aspx\\n * @override\\n */\\nXMLDOMDocument.prototype.loadXML = function(xmlString) {};\\n\\n/**\\n * @param {string} id\\n * @return {Node}\\n * @see http://msdn.microsoft.com/en-us/library/ms766397(VS.85).aspx\\n */\\nXMLDOMDocument.prototype.nodeFromID = function(id) {};\\n\\n//==============================================================================\\n// XMLNode methods and properties\\n// In a real DOM hierarchy, XMLDOMDocument inherits from XMLNode and Document.\\n// Since we can\'t express that in our type system, we put XMLNode properties\\n// on Node.\\n\\n/**\\n * @type {string}\\n * @see http://msdn.m'; +a.a+="icrosoft.com/en-us/library/ms767570(VS.85).aspx\\n */\\nNode.prototype.baseName;\\n\\n/**\\n * @type {?string}\\n * @see http://msdn.microsoft.com/en-us/library/ms762763(VS.85).aspx\\n */\\nNode.prototype.dataType;\\n\\n/**\\n * @type {Node}\\n * @see http://msdn.microsoft.com/en-us/library/ms764733(VS.85).aspx\\n */\\nNode.prototype.definition;\\n\\n/**\\n * IE5 used document instead of ownerDocument.\\n * Old versions of WebKit used document instead of contentDocument.\\n * @type {Document}\\n */\\nNode.prototype.document;\\n\\n\\n/**\\n * Inserts the given HTML text into the element at the location.\\n * @param {string} sWhere Where to insert the HTML text, one of 'beforeBegin',\\n * 'afterBegin', 'beforeEnd', 'afterEnd'.\\n * @param {!TrustedHTML|string} sText HTML text to insert.\\n * @see http://msdn.microsoft.com/en-us/library/ms536452(VS.85).aspx\\n * @return {undefined}\\n */\\nNode.prototype.insertAdjacentHTML = function(sWhere, sText) {};\\n\\n\\n/**\\n * Inserts the given HTML Element into the node at the location.\\n * @param {string} sWhere Where to insert the HTML text, one of 'beforeBegin',\\n * 'afterBegin', 'beforeEnd', 'afterEnd'.\\n * @param {!Element} sElement DOM Element to insert.\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentElement\\n * @return {?Element} The element that was inserted, or null, if the\\n * insertion failed.\\n */\\nNode.prototype.insertAdjacentElement = function(sWhere, sElement) {};\\n\\n\\n/**\\n * @type {*}\\n * @see http://msdn.microsoft.com/en-us/library/ms762308(VS.85).aspx\\n */\\nNode.prototype.nodeTypedValue;\\n\\n/**\\n * @type {string}\\n * @see http://msdn.microsoft.com/en-us/library/ms757895(VS.85).aspx\\n */\\nNode.prototype.nodeTypeString;\\n\\n/**\\n * @type {boolean}\\n * @see http://msdn.microsoft.com/en-us/library/ms762237(VS.85).aspx\\n */\\nNode.prototype.parsed;\\n\\n/**\\n * @type {Element}\\n * @see http://msdn.microsoft.com/en-us/library/ms534327(VS.85).aspx\\n */\\nNode.prototype.parentElement;\\n\\n/**\\n * @type {boolean}\\n * @see http://msdn.microsoft.com/en-us/library/ms753816(VS.85).aspx\\n */\\nNode.prototype.specified;\\n\\n/**\\n * @type {string}\\n * @see http://msdn.microsoft.com/en-us/library/ms762687(VS.85).aspx\\n */\\nNode.prototype.text;\\n\\n/**\\n * @type {string}\\n * @see http://msdn.microsoft.com/en-us/library/ms755989(VS.85).aspx\\n */\\nNode.prototype.xml;\\n\\n/**\\n * @param {string} expression An XPath expression.\\n * @return {!NodeList}\\n * @see http://msdn.microsoft.com/en-us/library/ms754523(VS.85).aspx\\n * @nosideeffects\\n */\\nNode.prototype.selectNodes = function(expression) {};\\n\\n/**\\n * @param {string} expression An XPath expression.\\n * @return {Node}\\n * @see http://msdn.microsoft.com/en-us/library/ms757846(VS.85).aspx\\n * @nosideeffects\\n */\\nNode.prototype.selectSingleNode = function(expression) {};\\n\\n/**\\n * @param {Node} stylesheet XSLT stylesheet.\\n * @return {string}\\n * @see http://msdn.microsoft.com/en-us/library/ms761399(VS.85).aspx\\n * @nosideeffects\\n */\\nNode.prototype.transformNode = function(stylesheet) {};\\n\\n/**\\n * @param {Node} stylesheet XSLT stylesheet.\\n * @param {Object} outputObject\\n * @see http://msdn.microsoft.com/en-us/library/ms766561(VS.85).aspx\\n * @return {Object}\\n */\\nNode.prototype.transformNodeToObject =\\n function(stylesheet, outputObject) {};\\n\\n//==============================================================================\\n// Node methods\\n\\n/**\\n * @param {boolean=} opt_bRemoveChildren Whether to remove the entire sub-tree.\\n * Defaults to false.\\n * @return {Node} The object that was removed.\\n * @see http://msdn.microsoft.com/en-us/library/ms536708(VS.85).aspx\\n */\\nNode.prototype.removeNode = function(opt_bRemoveChildren) {};\\n\\n/**\\n * @constructor\\n */\\nfunction ClipboardData() {}\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms535220(VS.85).aspx\\n * @param {string=} opt_type Type of clipboard data to clear. 'Text' or\\n * 'URL' or 'File' or 'HTML' or 'Image'.\\n * @return {undefined}\\n */\\nClipboardData.prototype.clearData = function(opt_type) {};\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms535220(VS.85).aspx\\n * @param {string} type Type of clipboard data to set ('Text' or 'URL').\\n * @param {string} data Data to set\\n * @return {boolean} Whether the data were set correctly.\\n */\\nClipboardData.prototype.setData = function(type, data) {};\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms535220(VS.85).aspx\\n * @param {string} type Type of clipboard data to get ('Text' or 'URL').\\n * @return {string} The current data\\n */\\nClipboardData.prototype.getData = function(type) { };\\n\\n/**\\n * @type {!Window}\\n * @see https://developer.mozilla.org/en/DOM/window\\n */\\nvar window;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms535220(VS.85).aspx\\n * @type {ClipboardData}\\n */\\nWindow.prototype.clipboardData;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms533724(VS.85).aspx\\n */\\nWindow.prototype.dialogHeight;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms533725(VS.85).aspx\\n */\\nWindow.prototype.dialogLeft;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms533726(VS.85).aspx\\n */\\nWindow.prototype.dialogTop;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms533727(VS.85).aspx\\n */\\nWindow.prototype.dialogWidth;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms535863(VS.85).aspx\\n */\\nWindow.prototype.event;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/cc197012(VS.85).aspx\\n */\\nWindow.prototype.maxConnectionsPer1_0Server;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/cc197013(VS.85).aspx\\n */\\nWindow.prototype.maxConnectionsPerServer;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms534198(VS.85).aspx\\n */\\nWindow.prototype.offscreenBuffering;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms534389(VS.85).aspx\\n * @type {number|undefined}\\n */\\nWindow.prototype.screenLeft;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms534389(VS.85).aspx\\n * @type {number|undefined}\\n */\\nWindow.prototype.screenTop;\\n\\n// Functions\\n\\n/**\\n * @param {string} event\\n * @param {Function} handler\\n * @see http://msdn.microsoft.com/en-us/library/ms536343(VS.85).aspx\\n */\\nWindow.prototype.attachEvent;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536392(VS.85).aspx\\n */\\nWindow.prototype.createPopup;\\n\\n/**\\n * @param {string} event\\n * @param {Function} handler\\n * @see http://msdn.microsoft.com/en-us/library/ms536411(VS.85).aspx\\n */\\nWindow.prototype.detachEvent;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536420(VS.85).aspx\\n */\\nWindow.prototype.execScript;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536425(VS.85).aspx\\n */\\nWindow.prototype.focus;\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @see http://msdn.microsoft.com/en-us/library/ms536618(VS.85).aspx\\n * @return {undefined}\\n */\\nWindow.prototype.moveBy = function(x, y) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @see http://msdn.microsoft.com/en-us/library/ms536626(VS.85).aspx\\n * @return {undefined}\\n */\\nWindow.prototype.moveTo = function(x, y) {};\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536638(VS.85).aspx\\n */\\nWindow.prototype.navigate;\\n\\n/**\\n * @param {*=} opt_url\\n * @param {string=} opt_windowName\\n * @param {string=} opt_windowFeatures\\n * @param {boolean=} opt_replace\\n * @return {Window}\\n * @see http://msdn.microsoft.com/en-us/library/ms536651(VS.85).aspx\\n */\\nWindow.prototype.open = function(opt_url, opt_windowName, opt_windowFeatures,\\n opt_replace) {};\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536672(VS.85).aspx\\n * @return {undefined}\\n */\\nWindow.prototype.print = function() {};\\n\\n/**\\n * @param {number} width\\n * @param {number} height\\n * @see http://msdn.microsoft.com/en-us/library/ms536722(VS.85).aspx\\n * @return {undefined}\\n */\\nWindow.prototype.resizeBy = function(width, height) {};\\n\\n/**\\n * @param {number} width\\n * @param {number} height\\n * @see http://msdn.microsoft.com/en-us/library/ms536723(VS.85).aspx\\n * @return {undefined}\\n */\\nWindow.prototype.resizeTo = function(width, height) {};\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536738(VS.85).aspx\\n */\\nWindow.prototype.setActive;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536758(VS.85).aspx\\n */\\nWindow.prototype.showHelp;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536761(VS.85).aspx\\n */\\nWindow.prototype.showModelessDialog;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms535246%28v=vs.85%29.aspx\\n * @const {!Object}\\n */\\nWindow.prototype.external;\\n\\n/**\\n * @see https://msdn.microsoft.com/en-us/ie/dn265046(v=vs.94)\\n * @const {!Object}\\n */\\nWindow.prototype.msCrypto;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms535864(VS.85).aspx\\n * @param {number|string} delta The number of entries to go back, or\\n * the URL to which to go back. (URL form is supported only in IE)\\n * @return {undefined}\\n */\\nHistory.prototype.go = function(delta) {};\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms535864(VS.85).aspx\\n * @param {number=} opt_distance The number of entries to go back\\n * (Mozilla doesn't support distance -- use #go instead)\\n * @return {undefined}\\n */\\nHistory.prototype.back = function(opt_distance) {};\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms535864(VS.85).aspx\\n * @type {number}\\n */\\nHistory.prototype.length;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms535864(VS.85).aspx\\n * @return {undefined}\\n */\\nHistory.prototype.forward = function() {};\\n\\n/**\\n * @type {boolean}\\n * @implicitCast\\n * @see http://msdn.microsoft.com/en-us/library/ie/ms533072(v=vs.85).aspx\\n */\\nHTMLFrameElement.prototype.allowTransparency;\\n\\n/**\\n * @type {Window}\\n * @see http://msdn.microsoft.com/en-us/library/ms533692(VS.85).aspx\\n */\\nHTMLFrameElement.prototype.contentWindow;\\n\\n/**\\n * @type {boolean}\\n * @implicitCast\\n * @see http://msdn.microsoft.com/en-us/library/ie/ms533072(v=vs.85).aspx\\n */\\nHTMLIFrameElement.prototype.allowTransparency;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536385(VS.85).aspx\\n */\\nHTMLBodyElement.prototype.createControlRange;\\n\\n/**\\n * @type {string}\\n * @see http://msdn.microsoft.com/en-us/library/ms534359(VS.85).aspx\\n */\\nHTMLScriptElement.prototype.readyState;\\n\\n/**\\n * @type {string}\\n * @see http://msdn.microsoft.com/en-us/library/ms534359(VS.85).aspx\\n */\\nHTMLIFrameElement.prototype.readyState;\\n\\n/**\\n * @type {string}\\n * @see http://msdn.microsoft.com/en-us/library/ms534359(VS.85).aspx\\n */\\nHTMLImageElement.prototype.readyState;\\n\\n/**\\n * @type {string}\\n * @see http://msdn.microsoft.com/en-us/library/ms534359(VS.85).aspx\\n */\\nHTMLObjectElement.prototype.readyState;\\n\\n\\n/**\\n * @constructor\\n */\\nfunction ControlRange() {}\\n\\nControlRange.prototype.add;\\nControlRange.prototype.addElement;\\nControlRange.prototype.execCommand;\\nControlRange.prototype.item;\\nControlRange.prototype.queryCommandEnabled;\\nControlRange.prototype.queryCommandIndeterm;\\nControlRange.prototype.queryCommandState;\\nControlRange.prototype.queryCommandSupported;\\nControlRange.prototype.queryCommandValue;\\nControlRange.prototype.remove;\\nControlRange.prototype.scrollIntoView;\\nControlRange.prototype.select;\\n\\n/**\\n * @constructor\\n * @see http://msdn.microsoft.com/en-us/library/ms535872.aspx\\n */\\nfunction TextRange() {}\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms533538(VS.85).aspx\\n */\\nTextRange.prototype.boundingHeight;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms533539(VS.85).aspx\\n */\\nTextRange.prototype.boundingLeft;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms533540(VS.85).aspx\\n */\\nTextRange.prototype.boundingTop;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms533541(VS.85).aspx\\n */\\nTextRange.prototype.boundingWidth;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms533874(VS.85).aspx\\n */\\nTextRange.prototype.htmlText;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms534200(VS.85).aspx\\n */\\nTextRange.prototype.offsetLeft;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms534303(VS.85).aspx\\n */\\nTextRange.prototype.offsetTop;\\n\\n/**\\n * @type {string}\\n * @see http://msdn.microsoft.com/en-us/library/ms534676(VS.85).aspx\\n */\\nTextRange.prototype.text;\\n\\n/**\\n * @param {boolean=} opt_toStart\\n * @see http://msdn.microsoft.com/en-us/library/ms536371(VS.85).aspx\\n */\\nTextRange.prototype.collapse;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536373(VS.85).aspx\\n */\\nTextRange.prototype.compareEndPoints;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536416(VS.85).aspx\\n */\\nTextRange.prototype.duplicate;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536419(VS.85).aspx\\n */\\nTextRange.prototype.execCommand;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536421(VS.85).aspx\\n */\\nTextRange.prototype.expand;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536422(VS.85).aspx\\n */\\nTextRange.prototype.findText;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536432(VS.85).aspx\\n */\\nTextRange.prototype.getBookmark;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536433(VS.85).aspx\\n */\\nTextRange.prototype.getBoundingClientRect;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536435(VS.85).aspx\\n */\\nTextRange.prototype.getClientRects;\\n\\n/**\\n * @param {TextRange|ControlRange} range\\n * @return {boolean}\\n * @see http://msdn.microsoft.com/en-us/library/ms536450(VS.85).aspx\\n */\\nTextRange.prototype.inRange;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536458(VS.85).aspx\\n */\\nTextRange.prototype.isEqual;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536616(VS.85).aspx\\n */\\nTextRange.prototype.move;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536620(VS.85).aspx\\n */\\nTextRange.prototype.moveEnd;\\n\\n/**\\n * @param {string} unit\\n * @param {number=} opt_count\\n * @see http://msdn.microsoft.com/en-us/library/ms536623(VS.85).aspx\\n */\\nTextRange.prototype.moveStart;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536628(VS.85).aspx\\n */\\nTextRange.prototype.moveToBookmark;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536630(VS.85).aspx\\n */\\nTextRange.prototype.moveToElementText;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536632(VS.85).aspx\\n */\\nTextRange.prototype.moveToPoint;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536654(VS.85).aspx\\n * @return {?Element}\\n */\\nTextRange.prototype.parentElement;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536656(VS.85).aspx\\n */\\nTextRange.prototype.pasteHTML;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536676(VS.85).aspx\\n */\\nTextRange.prototype.queryCommandEnabled;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536678(VS.85).aspx\\n */\\nTextRange.prototype.queryCommandIndeterm;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536679(VS.85).aspx\\n */\\nTextRange.prototype.queryCommandState;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536681(VS.85).aspx\\n */\\nTextRange.prototype.queryCommandSupported;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536683(VS.85).aspx\\n */\\nTextRange.prototype.queryCommandValue;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536730(VS.85).aspx\\n */\\nTextRange.prototype.scrollIntoView;\\n\\n/**\\n * @return {undefined}\\n * @see http://msdn.microsoft.com/en-us/library/ms536735(VS.85).aspx\\n */\\nTextRange.prototype.select = function() {};\\n\\n/**\\n * @param {string} how\\n * @param {TextRange|ControlRange} sourceRange\\n * @see http://msdn.microsoft.com/en-us/library/ms536745(VS.85).aspx\\n */\\nTextRange.prototype.setEndPoint;\\n\\n/**\\n * @return {undefined}\\n * @see http://msdn.microsoft.com/en-us/library/ms536418(VS.85).aspx\\n */\\nSelection.prototype.clear = function() {};\\n\\n/**\\n * @return {TextRange|ControlRange}\\n * @see http://msdn.microsoft.com/en-us/library/ms536394(VS.85).aspx\\n */\\nSelection.prototype.createRange = function() {};\\n\\n/**\\n * @return {Array}\\n * @see http://msdn.microsoft.com/en-us/library/ms536396(VS.85).aspx\\n */\\nSelection.prototype.createRangeCollection = function() {};\\n\\n/**\\n * @constr"; +a.a+='uctor\\n * @see http://msdn.microsoft.com/en-us/library/ms537447(VS.85).aspx\\n */\\nfunction controlRange() {}\\n\\n\\nDocument.prototype.loadXML;\\n\\n\\n// http://msdn.microsoft.com/en-us/library/ms531073(VS.85).aspx\\n\\n/**\\n * @type {!Element}\\n * @see http://msdn.microsoft.com/en-us/library/ms533065(VS.85).aspx\\n */\\nDocument.prototype.activeElement;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms533553(VS.85).aspx\\n */\\nDocument.prototype.charset;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms533693(VS.85).aspx\\n */\\nDocument.prototype.cookie;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms533714(VS.85).aspx\\n */\\nDocument.prototype.defaultCharset;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms533731(VS.85).aspx\\n */\\nDocument.prototype.dir;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/cc196988(VS.85).aspx\\n */\\nDocument.prototype.documentMode;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms533747(VS.85).aspx\\n */\\nDocument.prototype.expando;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms533750(VS.85).aspx\\n */\\nDocument.prototype.fileCreatedDate;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms533751(VS.85).aspx\\n */\\nDocument.prototype.fileModifiedDate;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms533752(VS.85).aspx\\n */\\nDocument.prototype.fileSize;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms534331(VS.85).aspx\\n * @type {?Window}\\n */\\nDocument.prototype.parentWindow;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms534353(VS.85).aspx\\n */\\nDocument.prototype.protocol;\\n\\n/**\\n * @type {string}\\n * @see http://msdn.microsoft.com/en-us/library/ms534359(VS.85).aspx\\n */\\nHTMLDocument.prototype.readyState;\\n\\n/**\\n * @type {Selection}\\n * @see http://msdn.microsoft.com/en-us/library/ms535869(VS.85).aspx\\n */\\nDocument.prototype.selection;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms534704(VS.85).aspx\\n */\\nDocument.prototype.uniqueID;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms534709(VS.85).aspx\\n */\\nDocument.prototype.URLUnencoded;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms535155(VS.85).aspx\\n */\\nDocument.prototype.XMLDocument;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms535163(VS.85).aspx\\n */\\nDocument.prototype.XSLDocument;\\n\\n// functions\\n\\n/**\\n * @param {string} event\\n * @param {Function} handler\\n * @see http://msdn.microsoft.com/en-us/library/ms536343(VS.85).aspx\\n */\\nDocument.prototype.attachEvent;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536390(VS.85).aspx\\n */\\nDocument.prototype.createEventObject;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms531194(VS.85).aspx\\n */\\nDocument.prototype.createStyleSheet;\\n\\n/**\\n * @param {string} event\\n * @param {Function} handler\\n * @see http://msdn.microsoft.com/en-us/library/ms536411(VS.85).aspx\\n */\\nDocument.prototype.detachEvent;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536425(VS.85).aspx\\n */\\nDocument.prototype.focus;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536447(VS.85).aspx\\n * @return {boolean}\\n */\\nDocument.prototype.hasFocus = function() {};\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536614(VS.85).aspx\\n */\\nDocument.prototype.mergeAttributes;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536685(VS.85).aspx\\n */\\nDocument.prototype.recalc;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536689(VS.85).aspx\\n */\\nDocument.prototype.releaseCapture;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536738(VS.85).aspx\\n */\\nDocument.prototype.setActive;\\n\\n\\n// collections\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms537434(VS.85).aspx\\n */\\nDocument.prototype.all;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms537445(VS.85).aspx\\n */\\nDocument.prototype.childNodes;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms537459(VS.85).aspx\\n */\\nDocument.prototype.frames;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms537470(VS.85).aspx\\n */\\nDocument.prototype.namespaces;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms537487(VS.85).aspx\\n * @type {!HTMLCollection}\\n */\\nDocument.prototype.scripts;\\n\\n/**\\n * @param {string} sUrl\\n * @return {number}\\n * @see http://msdn.microsoft.com/en-us/library/ms535922(VS.85).aspx\\n */\\nElement.prototype.addBehavior = function(sUrl) {};\\n\\n/**\\n * @param {string} event\\n * @param {Function} handler\\n * @see http://msdn.microsoft.com/en-us/library/mm536343(v=vs.85).aspx\\n */\\nElement.prototype.attachEvent;\\n\\n/**\\n * @type {boolean}\\n * @see http://msdn.microsoft.com/en-us/library/ms533546(VS.85).aspx\\n */\\nElement.prototype.canHaveChildren;\\n\\n/**\\n * @type {string}\\n * @see http://msdn.microsoft.com/en-us/library/ms533559(v=vs.85).aspx\\n */\\nElement.prototype.classid;\\n\\n/**\\n * @param {number} iCoordX Integer that specifies the client window coordinate\\n * of x.\\n * @param {number} iCoordY Integer that specifies the client window coordinate\\n * of y.\\n * @return {string} The component of an element located at the specified\\n * coordinates.\\n * @see http://msdn.microsoft.com/en-us/library/ms536375(VS.85).aspx\\n * @nosideeffects\\n */\\nElement.prototype.componentFromPoint = function(iCoordX, iCoordY) {};\\n\\n\\n/**\\n * TODO(tjgq): Make this string once existing usages have been migrated.\\n * @type {string|boolean}\\n * @see http://msdn.microsoft.com/en-us/library/ms533690(VS.85).aspx\\n */\\nElement.prototype.contentEditable;\\n\\n/**\\n * @return {TextRange}\\n * @see http://msdn.microsoft.com/en-us/library/ms536401(VS.85).aspx\\n */\\nElement.prototype.createTextRange;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms535231(VS.85).aspx\\n * @type {?CSSStyleDeclaration}\\n */\\nElement.prototype.currentStyle;\\n\\n/**\\n * @param {string} event\\n * @param {Function} handler\\n * @see http://msdn.microsoft.com/en-us/library/ie/ms536411(v=vs.85).aspx\\n */\\nElement.prototype.detachEvent;\\n\\n/**\\n * @param {string=} opt_action\\n * @see http://msdn.microsoft.com/en-us/library/ms536414%28VS.85%29.aspx\\n * @return {undefined}\\n */\\nElement.prototype.doScroll = function(opt_action) {};\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536423(VS.85).aspx\\n */\\nElement.prototype.fireEvent;\\n\\n/**\\n * @type {boolean}\\n * @see http://msdn.microsoft.com/en-us/library/ms533783(VS.85).aspx\\n */\\nElement.prototype.hideFocus;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms533899.aspx\\n */\\nElement.prototype.innerText;\\n\\n/**\\n * @type {boolean}\\n * @see http://msdn.microsoft.com/en-us/library/ms537838(VS.85).aspx\\n */\\nElement.prototype.isContentEditable;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms531395(v=vs.85).aspx\\n * NOTE: Left untyped to avoid conflict with subclasses.\\n */\\nElement.prototype.load;\\n\\n/**\\n * @param {number} pointerId Id of the pointer that is assign to the element.\\n * @see http://msdn.microsoft.com/en-us/library/ie/hh771882(v=vs.85).aspx\\n * @return {undefined}\\n */\\nElement.prototype.msSetPointerCapture = function(pointerId) {};\\n\\n/**\\n * @param {number} pointerId\\n * @see http://msdn.microsoft.com/en-us/library/ie/hh771880.aspx\\n * @return {undefined}\\n */\\nElement.prototype.msReleasePointerCapture = function(pointerId) {};\\n\\n/**\\n * @type {?function(Event)}\\n * @see http://msdn.microsoft.com/en-us/library/ms536903(v=vs.85).aspx\\n */\\nElement.prototype.onbeforedeactivate;\\n\\n/**\\n * @type {?function(Event)}\\n * @see http://msdn.microsoft.com/en-us/library/ms536945(VS.85).aspx\\n */\\nElement.prototype.onmouseenter;\\n\\n/**\\n * @type {?function(Event)}\\n * @see http://msdn.microsoft.com/en-us/library/ms536946(VS.85).aspx\\n */\\nElement.prototype.onmouseleave;\\n\\n/**\\n * @type {?function(Event)}\\n * @see http://msdn.microsoft.com/en-us/library/ms536969(VS.85).aspx\\n */\\nElement.prototype.onselectstart;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://msdn.microsoft.com/en-us/library/aa752326(VS.85).aspx\\n */\\nElement.prototype.outerHTML;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536689(VS.85).aspx\\n * @return {undefined}\\n */\\nElement.prototype.releaseCapture = function() {};\\n\\n/**\\n * @param {number} iID\\n * @return {boolean}\\n * @see http://msdn.microsoft.com/en-us/library/ms536700(VS.85).aspx\\n */\\nElement.prototype.removeBehavior = function(iID) {};\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/aa703996(VS.85).aspx\\n */\\nElement.prototype.runtimeStyle;\\n\\n/**\\n * @param {string} sStoreName The arbitrary name assigned to a persistent object\\n * in a UserData store.\\n * @see http://msdn.microsoft.com/en-us/library/ms531403(v=vs.85).aspx\\n * @return {undefined}\\n */\\nElement.prototype.save = function(sStoreName) {};\\n\\n/**\\n * @param {boolean=} opt_bContainerCapture Events originating in a container are\\n * captured by the container. Defaults to true.\\n * @see http://msdn.microsoft.com/en-us/library/ms536742(VS.85).aspx\\n * @return {undefined}\\n */\\nElement.prototype.setCapture = function(opt_bContainerCapture) {};\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms534635(VS.85).aspx\\n */\\nElement.prototype.sourceIndex;\\n\\n/**\\n * @type {string}\\n * @see http://msdn.microsoft.com/en-us/library/ms537840.aspx\\n */\\nElement.prototype.unselectable;\\n\\n/**\\n * @constructor\\n * @see http://msdn.microsoft.com/en-us/library/aa752462(v=vs.85).aspx\\n */\\nfunction HTMLFiltersCollection() {}\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/aa752463(v=vs.85).aspx\\n * @type {number}\\n */\\nHTMLFiltersCollection.prototype.length;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms537452(v=vs.85).aspx\\n * @type {HTMLFiltersCollection}\\n */\\nElement.prototype.filters;\\n\\n/**\\n * @constructor\\n * @see http://msdn.microsoft.com/en-us/library/ms532853(v=vs.85).aspx\\n */\\nfunction HTMLFilter() {}\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms532954(v=vs.85).aspx\\n * @return {undefined}\\n */\\nHTMLFilter.prototype.apply = function() {};\\n\\n/**\\n * @constructor\\n * @extends {HTMLFilter}\\n * @see http://msdn.microsoft.com/en-us/library/ms532967(v=vs.85).aspx\\n */\\nfunction AlphaFilter() {}\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms532910(v=vs.85).aspx\\n * @type {number}\\n */\\nAlphaFilter.prototype.Opacity;\\n\\n/**\\n * @constructor\\n * @extends {HTMLFilter}\\n * @see http://msdn.microsoft.com/en-us/library/ms532969(v=vs.85).aspx\\n */\\nfunction AlphaImageLoaderFilter() {}\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms532920(v=vs.85).aspx\\n * @type {string}\\n */\\nAlphaImageLoaderFilter.prototype.sizingMethod;\\n\\n/**\\n * @constructor\\n * @see http://msdn.microsoft.com/en-us/library/ms535866(VS.85).aspx\\n */\\nfunction Location() {}\\n\\n/**\\n * @see http://trac.webkit.org/changeset/113945\\n * @type {DOMStringList}\\n */\\nLocation.prototype.ancestorOrigins;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms533775(VS.85).aspx\\n * @type {string}\\n */\\nLocation.prototype.hash;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms533784(VS.85).aspx\\n * @type {string}\\n */\\nLocation.prototype.host;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms533785(VS.85).aspx\\n * @type {string}\\n */\\nLocation.prototype.hostname;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms533867(VS.85).aspx\\n * @implicitCast\\n * @type {string}\\n */\\nLocation.prototype.href;\\n\\n/**\\n * @see https://docs.google.com/document/view?id=1r_VTFKApVOaNIkocrg0z-t7lZgzisTuGTXkdzAk4gLU&hl=en\\n * @type {string}\\n */\\nLocation.prototype.origin;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms534332(VS.85).aspx\\n * @type {string}\\n */\\nLocation.prototype.pathname;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms534342(VS.85).aspx\\n * @type {string}\\n */\\nLocation.prototype.port;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms534353(VS.85).aspx\\n * @type {string}\\n */\\nLocation.prototype.protocol;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms534620(VS.85).aspx\\n * @type {string}\\n */\\nLocation.prototype.search;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ms536342(VS.85).aspx\\n * @param {!TrustedURL|string} url\\n * @return {undefined}\\n */\\nLocation.prototype.assign = function(url) {};\\n\\n/**\\n * @param {boolean=} opt_forceReload If true, reloads the page from\\n * the server. Defaults to false.\\n * @see http://msdn.microsoft.com/en-us/library/ms536691(VS.85).aspx\\n * @return {undefined}\\n */\\nLocation.prototype.reload = function(opt_forceReload) {};\\n\\n/**\\n * @param {!TrustedURL|string} url\\n * @see http://msdn.microsoft.com/en-us/library/ms536712(VS.85).aspx\\n * @return {undefined}\\n */\\nLocation.prototype.replace = function(url) {};\\n\\n\\n// For IE, returns an object representing key-value pairs for all the global\\n// variables prefixed with str, e.g. test*\\n\\n/** @param {*=} opt_str\\n */\\nfunction RuntimeObject(opt_str) {}\\n\\n\\n/**\\n * @type {StyleSheet}\\n * @see http://msdn.microsoft.com/en-us/library/dd347030(VS.85).aspx\\n */\\nHTMLStyleElement.prototype.styleSheet;\\n\\n\\n/**\\n * IE implements Cross Origin Resource Sharing (cross-domain XMLHttpRequests)\\n * via the XDomainRequest object.\\n *\\n * @constructor\\n * @see http://msdn.microsoft.com/en-us/library/cc288060(v=vs.85).aspx\\n * @see http://www.w3.org/TR/cors/\\n */\\nfunction XDomainRequest() {}\\n\\n/**\\n * Aborts the request.\\n * @see http://msdn.microsoft.com/en-us/library/cc288129(v=vs.85).aspx\\n * @return {undefined}\\n */\\nXDomainRequest.prototype.abort = function() {};\\n\\n/**\\n * Sets the method and URL for the request.\\n * @param {string} bstrMethod Either \\"GET\\" or \\"POST\\"\\n * @param {string} bstrUrl The target URL\\n * @see http://msdn.microsoft.com/en-us/library/cc288168(v=vs.85).aspx\\n * @return {undefined}\\n */\\nXDomainRequest.prototype.open = function(bstrMethod, bstrUrl) {};\\n\\n/**\\n * Sends the request.\\n * @param {string=} varBody The POST body to send to the server. If omitted,\\n * the behavior is identical to sending an empty string.\\n * @see http://msdn.microsoft.com/en-us/library/cc288207(v=vs.85).aspx\\n * @return {undefined}\\n */\\nXDomainRequest.prototype.send = function(varBody) {};\\n\\n/**\\n * Called if the request could not be completed. Note that error information is\\n * not available.\\n * @see http://msdn.microsoft.com/en-us/library/ms536930%28v=VS.85%29.aspx\\n * @type {?function()}\\n */\\nXDomainRequest.prototype.onerror;\\n\\n/**\\n * Called when the response has finished.\\n * @see http://msdn.microsoft.com/en-us/library/ms536942%28v=VS.85%29.aspx\\n * @type {?function()}\\n */\\nXDomainRequest.prototype.onload;\\n\\n/**\\n * Called every time part of the response has been received.\\n * @see http://msdn.microsoft.com/en-us/library/cc197058%28v=VS.85%29.aspx\\n * @type {?function()}\\n */\\nXDomainRequest.prototype.onprogress;\\n\\n/**\\n * Called if the timeout period has elapsed.\\n * @see http://msdn.microsoft.com/en-us/library/cc197061%28v=VS.85%29.aspx\\n * @type {?function()}\\n */\\nXDomainRequest.prototype.ontimeout;\\n\\n/**\\n * The current response body.\\n * @see http://msdn.microsoft.com/en-us/library/cc287956%28v=VS.85%29.aspx\\n * @type {string}\\n */\\nXDomainRequest.prototype.responseText;\\n\\n/**\\n * The timeout (in milliseconds) for the request.\\n * @type {number}\\n */\\nXDomainRequest.prototype.timeout;\\n\\n/**\\n * The Content-Type of the response, or an empty string.\\n * @type {string}\\n */\\nXDomainRequest.prototype.contentType;\\n\\n/**\\n * @type {string}\\n * @see http://msdn.microsoft.com/en-us/library/ms533542(v=vs.85).aspx\\n */\\nNavigator.prototype.browserLanguage;\\n\\n/**\\n * @type {number}\\n * @see https://msdn.microsoft.com/en-us/library/ie/hh772144(v=vs.85).aspx\\n */\\nNavigator.prototype.msMaxTouchPoints;\\n\\n/**\\n * @type {boolean}\\n * @see http://blogs.msdn.com/b/ie/archive/2011/09/20/touch-input-for-ie10-and-metro-style-apps.aspx\\n */\\nNavigator.prototype.msPointerEnabled;\\n\\n/**\\n * @param {(!File|!Blob)} blob\\n * @param {string=} opt_defaultName\\n * @return {boolean}\\n * @see https://msdn.microsoft.com/en-us/library/hh772331(v=vs.85).aspx\\n */\\nNavigator.prototype.msSaveBlob = function(blob, opt_defaultName) {};\\n\\n/**\\n * @param {(!File|!Blob)} blob\\n * @param {string=} opt_defaultName\\n * @return {boolean}\\n * @see https://msdn.microsoft.com/en-us/library/hh772332(v=vs.8'; +a.a+='5).aspx\\n */\\nNavigator.prototype.msSaveOrOpenBlob = function(blob, opt_defaultName) {};\\n\\n/**\\n * @type {number}\\n * @see http://msdn.microsoft.com/en-us/library/ms533721(v=vs.85).aspx\\n */\\nScreen.prototype.deviceXDPI;\\n\\n/**\\n * @type {number}\\n * @see http://msdn.microsoft.com/en-us/library/ms534128%28v=vs.85%29.aspx\\n */\\nScreen.prototype.logicalXDPI;\\n\\n/**\\n * @type {number}\\n * @see http://msdn.microsoft.com/en-us/library/ms534130%28v=vs.85%29.aspx\\n */\\nScreen.prototype.logicalYDPI;\\n","externs/webkit_dom.js":"/*\\n * Copyright 2008 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for all the extensions over W3C\'s DOM\\n * specification by WebKit. This file depends on w3c_dom2.js.\\n * All the provided definitions has been type annotated\\n *\\n * @externs\\n * @author acleung@google.com (Alan Leung)\\n */\\n\\n\\n/**\\n * @param {boolean=} opt_center\\n * @see https://bugzilla.mozilla.org/show_bug.cgi?id=403510\\n * @return {undefined}\\n */\\nElement.prototype.scrollIntoViewIfNeeded = function(opt_center) {};\\n\\n/**\\n * @constructor\\n * @see https://cs.chromium.org/search/?q=%22interface+MemoryInfo%22+file:idl+file:WebKit+package:chromium&type=cs\\n */\\nfunction MemoryInfo() {};\\n\\n/** @type {number} */\\nMemoryInfo.prototype.totalJSHeapSize;\\n\\n/** @type {number} */\\nMemoryInfo.prototype.usedJSHeapSize;\\n\\n/** @type {number} */\\nMemoryInfo.prototype.jsHeapSizeLimit;\\n\\n/**\\n * @constructor\\n * @see http://trac.webkit.org/browser/trunk/Source/WebCore/inspector/ScriptProfileNode.idl\\n */\\nfunction ScriptProfileNode() {};\\n\\n/** @type {string} */\\nScriptProfileNode.prototype.functionName;\\n\\n/** @type {string} */\\nScriptProfileNode.prototype.url;\\n\\n/** @type {number} */\\nScriptProfileNode.prototype.lineNumber;\\n\\n/** @type {number} */\\nScriptProfileNode.prototype.totalTime;\\n\\n/** @type {number} */\\nScriptProfileNode.prototype.selfTime;\\n\\n/** @type {number} */\\nScriptProfileNode.prototype.numberOfCalls;\\n\\n/** @type {Array} */\\nScriptProfileNode.prototype.children;\\n\\n/** @type {boolean} */\\nScriptProfileNode.prototype.visible;\\n\\n/** @type {number} */\\nScriptProfileNode.prototype.callUID;\\n\\n/**\\n * @constructor\\n * @see http://trac.webkit.org/browser/trunk/Source/WebCore/inspector/ScriptProfile.idl\\n */\\nfunction ScriptProfile() {};\\n\\n/** @type {string} */\\nScriptProfile.prototype.title;\\n\\n/** @type {number} */\\nScriptProfile.prototype.uid;\\n\\n/** @type {ScriptProfileNode} */\\nScriptProfile.prototype.head;\\n\\n/**\\n * @constructor\\n * @see https://console.spec.whatwg.org/\\n */\\nfunction Console() {};\\n\\n/**\\n * @param {*} condition\\n * @param {...*} var_args\\n * @return {undefined}\\n */\\nConsole.prototype.assert = function(condition, var_args) {};\\n\\n/**\\n * @param {...*} var_args\\n * @return {undefined}\\n */\\nConsole.prototype.error = function(var_args) {};\\n\\n/**\\n * @param {...*} var_args\\n * @return {undefined}\\n */\\nConsole.prototype.info = function(var_args) {};\\n\\n/**\\n * @param {...*} var_args\\n * @return {undefined}\\n */\\nConsole.prototype.log = function(var_args) {};\\n\\n/**\\n * @param {...*} var_args\\n * @return {undefined}\\n */\\nConsole.prototype.warn = function(var_args) {};\\n\\n/**\\n * @param {...*} var_args\\n * @return {undefined}\\n */\\nConsole.prototype.debug = function(var_args) {};\\n\\n/**\\n * @param {*} value\\n * @return {undefined}\\n */\\nConsole.prototype.dir = function(value) {};\\n\\n/**\\n * @param {...*} var_args\\n * @return {undefined}\\n */\\nConsole.prototype.dirxml = function(var_args) {};\\n\\n/**\\n * @param {!Object} data\\n * @param {*=} opt_columns\\n * @return {undefined}\\n */\\nConsole.prototype.table = function(data, opt_columns) {};\\n\\n/**\\n * @param {...*} var_args\\n * @return {undefined}\\n */\\nConsole.prototype.trace = function(var_args) {};\\n\\n/**\\n * @param {*} value\\n * @return {undefined}\\n */\\nConsole.prototype.count = function(value) {};\\n\\n/**\\n * @param {*} value\\n * @return {undefined}\\n */\\nConsole.prototype.markTimeline = function(value) {};\\n\\n/**\\n * @param {string=} opt_title\\n * @return {undefined}\\n */\\nConsole.prototype.profile = function(opt_title) {};\\n\\n/** @type {Array} */\\nConsole.prototype.profiles;\\n\\n/**\\n * @param {string=} opt_title\\n * @return {undefined}\\n */\\nConsole.prototype.profileEnd = function(opt_title) {};\\n\\n/**\\n * @param {string} name\\n * @return {undefined}\\n */\\nConsole.prototype.time = function(name) {};\\n\\n/**\\n * @param {string} name\\n * @return {undefined}\\n */\\nConsole.prototype.timeEnd = function(name) {};\\n\\n/**\\n * @param {*} value\\n * @return {undefined}\\n */\\nConsole.prototype.timeStamp = function(value) {};\\n\\n/**\\n * @param {...*} var_args\\n * @return {undefined}\\n */\\nConsole.prototype.group = function(var_args) {};\\n\\n/**\\n * @param {...*} var_args\\n * @return {undefined}\\n */\\nConsole.prototype.groupCollapsed = function(var_args) {};\\n\\nConsole.prototype.groupEnd = function() {};\\n\\nConsole.prototype.clear = function() {};\\n\\n/** @type {MemoryInfo} */\\nConsole.prototype.memory;\\n\\n/** @type {!Console} */\\nWindow.prototype.console;\\n\\n/**\\n * @type {!Console}\\n * @suppress {duplicate}\\n */\\nvar console;\\n\\n/**\\n * @type {number}\\n * @see http://developer.android.com/reference/android/webkit/WebView.html\\n */\\nWindow.prototype.devicePixelRatio;\\n\\n/** @type {Node} */\\nSelection.prototype.baseNode;\\n\\n/** @type {number} */\\nSelection.prototype.baseOffset;\\n\\n/** @type {Node} */\\nSelection.prototype.extentNode;\\n\\n/** @type {number} */\\nSelection.prototype.extentOffset;\\n\\n/** @type {string} */\\nSelection.prototype.type;\\n\\n/**\\n * @return {undefined}\\n */\\nSelection.prototype.empty = function() {};\\n\\n/**\\n * @param {Node} baseNode\\n * @param {number} baseOffset\\n * @param {Node} extentNode\\n * @param {number} extentOffset\\n * @return {undefined}\\n */\\nSelection.prototype.setBaseAndExtent =\\n function(baseNode, baseOffset, extentNode, extentOffset) {};\\n\\n/**\\n * @param {string} alter\\n * @param {string} direction\\n * @param {string} granularity\\n * @return {undefined}\\n */\\nSelection.prototype.modify = function(alter, direction, granularity) {};\\n\\n/**\\n * @param {Element} element\\n * @param {string} pseudoElement\\n * @param {boolean=} opt_authorOnly\\n * @return {CSSRuleList}\\n * @nosideeffects\\n */\\nViewCSS.prototype.getMatchedCSSRules =\\n function(element, pseudoElement, opt_authorOnly) {};\\n\\n/**\\n * @param {string} contextId\\n * @param {string} name\\n * @param {number} width\\n * @param {number} height\\n * @nosideeffects\\n * @return {undefined}\\n */\\nDocument.prototype.getCSSCanvasContext =\\n function(contextId, name, width, height) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @return {?Range}\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/caretRangeFromPoint\\n */\\nDocument.prototype.caretRangeFromPoint = function(x, y) {};\\n\\n/**\\n * @return {!Promise}\\n * @nosideeffects\\n * @see https://webkit.org/blog/8124/introducing-storage-access-api\\n */\\nDocument.prototype.hasStorageAccess = function() {};\\n\\n/**\\n * @return {!Promise}\\n * @see https://webkit.org/blog/8124/introducing-storage-access-api\\n */\\nDocument.prototype.requestStorageAccess = function() {};\\n","externs/w3c_css.js":"/*\\n * Copyright 2008 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for W3C\'s CSS specification\\n * The whole file has been fully type annotated.\\n * http://www.w3.org/TR/DOM-Level-2-Style/css.html\\n * @externs\\n * @author acleung@google.com (Alan Leung)\\n * @author stevey@google.com (Steve Yegge)\\n *\\n * TODO(nicksantos): When there are no more occurrences of w3c_range.js and\\n * gecko_dom.js being included directly in BUILD files, bug dbeam to split the\\n * bottom part of this file into a separate externs.\\n */\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet\\n */\\nfunction StyleSheet() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet-type\\n */\\nStyleSheet.prototype.type;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet-disabled\\n */\\nStyleSheet.prototype.disabled;\\n\\n/**\\n * @type {Node}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet-owner\\n */\\nStyleSheet.prototype.ownerNode;\\n\\n/**\\n * @type {StyleSheet}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet-parentStyleSheet\\n */\\nStyleSheet.prototype.parentStyleSheet;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet-href\\n */\\nStyleSheet.prototype.href;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet-title\\n */\\nStyleSheet.prototype.title;\\n\\n/**\\n * @type {MediaList}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet-media\\n */\\nStyleSheet.prototype.media;\\n\\n/**\\n * @constructor\\n * @implements {IArrayLike}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheetList\\n */\\nfunction StyleSheetList() {}\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheetList-length\\n */\\nStyleSheetList.prototype.length;\\n\\n/**\\n * @param {number} index\\n * @return {StyleSheet}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheetList-item\\n */\\nStyleSheetList.prototype.item = function(index) {};\\n\\n/**\\n * @constructor\\n * @implements {IArrayLike}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-MediaList\\n */\\nfunction MediaList() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-MediaList-mediaText\\n */\\nMediaList.prototype.mediaText;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-MediaList-length\\n */\\nMediaList.prototype.length;\\n\\n/**\\n * @param {number} index\\n * @return {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-MediaList-item\\n */\\nMediaList.prototype.item = function(index) {};\\n\\n/**\\n * @interface\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-LinkStyle\\n */\\nfunction LinkStyle() {}\\n\\n/**\\n * @type {StyleSheet}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-LinkStyle-sheet\\n */\\nLinkStyle.prototype.sheet;\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet-DocumentStyle\\n */\\nfunction DocumentStyle() {}\\n\\n/**\\n * @type {StyleSheetList}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet-DocumentStyle-styleSheets\\n */\\nDocumentStyle.prototype.styleSheets;\\n\\n/**\\n * @constructor\\n * @extends {StyleSheet}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet\\n */\\nfunction CSSStyleSheet() {}\\n\\n/**\\n * @type {CSSRule}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet-ownerRule\\n */\\nCSSStyleSheet.prototype.ownerRule;\\n\\n/**\\n * @type {CSSRuleList}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet-cssRules\\n */\\nCSSStyleSheet.prototype.cssRules;\\n\\n/**\\n * @param {string} rule\\n * @param {number} index\\n * @return {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet-insertRule\\n */\\nCSSStyleSheet.prototype.insertRule = function(rule, index) {};\\n\\n/**\\n * @param {number} index\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet-deleteRule\\n * @return {undefined}\\n */\\nCSSStyleSheet.prototype.deleteRule = function(index) {};\\n\\n/**\\n * @constructor\\n * @implements {IArrayLike}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRuleList\\n */\\nfunction CSSRuleList() {}\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRuleList-length\\n */\\nCSSRuleList.prototype.length;\\n\\n/**\\n * @param {number} index\\n * @return {CSSRule}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRuleList-item\\n */\\nCSSRuleList.prototype.item = function(index) {};\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule\\n */\\nfunction CSSRule() {}\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule-ruleType\\n */\\nCSSRule.prototype.type;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule-cssText\\n */\\nCSSRule.prototype.cssText;\\n\\n/**\\n * @type {CSSStyleSheet}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule-sheet\\n */\\nCSSRule.prototype.parentStyleSheet;\\n\\n/**\\n * @type {CSSRule}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule-parentRule\\n */\\nCSSRule.prototype.parentRule;\\n\\n/**\\n * @type {CSSStyleDeclaration}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleRule\\n */\\nCSSRule.prototype.style;\\n\\n/**\\n * Indicates that the rule is a {@see CSSUnknownRule}.\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule-ruleType\\n */\\nCSSRule.UNKNOWN_RULE = 0;\\n\\n/**\\n * Indicates that the rule is a {@see CSSStyleRule}.\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule-ruleType\\n */\\nCSSRule.STYLE_RULE = 1;\\n\\n/**\\n * Indicates that the rule is a {@see CSSCharsetRule}.\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule-ruleType\\n */\\nCSSRule.CHARSET_RULE = 2;\\n\\n/**\\n * Indicates that the rule is a {@see CSSImportRule}.\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule-ruleType\\n */\\nCSSRule.IMPORT_RULE = 3;\\n\\n/**\\n * Indicates that the rule is a {@see CSSMediaRule}.\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule-ruleType\\n */\\nCSSRule.MEDIA_RULE = 4;\\n\\n/**\\n * Indicates that the rule is a {@see CSSFontFaceRule}.\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule-ruleType\\n */\\nCSSRule.FONT_FACE_RULE = 5;\\n\\n/**\\n * Indicates that the rule is a {@see CSSPageRule}.\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule-ruleType\\n */\\nCSSRule.PAGE_RULE = 6;\\n\\n/**\\n * @constructor\\n * @extends {CSSRule}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleRule\\n */\\nfunction CSSStyleRule() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleRule-selectorText\\n */\\nCSSStyleRule.prototype.selectorText;\\n\\n/**\\n * @type {CSSStyleDeclaration}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleRule-style\\n */\\nCSSStyleRule.prototype.style;\\n\\n/**\\n * @constructor\\n * @extends {CSSRule}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSMediaRule\\n */\\nfunction CSSMediaRule() {}\\n\\n/**\\n * @type {MediaList}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSMediaRule-mediaTy'; +a.a+="pes\\n */\\nCSSMediaRule.prototype.media;\\n\\n/**\\n * @type {CSSRuleList}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSMediaRule-cssRules\\n */\\nCSSMediaRule.prototype.cssRules;\\n\\n/**\\n * @param {string} rule\\n * @param {number} index\\n * @return {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSMediaRule-insertRule\\n */\\nCSSMediaRule.prototype.insertRule = function(rule, index) {};\\n\\n/**\\n * @param {number} index\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSMediaRule-deleteRule\\n * @return {undefined}\\n */\\nCSSMediaRule.prototype.deleteRule = function(index) {};\\n\\n/**\\n * @constructor\\n * @extends {CSSRule}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSFontFaceRule\\n */\\nfunction CSSFontFaceRule() {}\\n\\n/**\\n * @type {CSSStyleDeclaration}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSFontFaceRule-style\\n */\\nCSSFontFaceRule.prototype.style;\\n\\n/**\\n * @constructor\\n * @extends {CSSRule}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPageRule\\n */\\nfunction CSSPageRule() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPageRule-name\\n */\\nCSSPageRule.prototype.selectorText;\\n\\n/**\\n * @type {CSSStyleDeclaration}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPageRule-style\\n */\\nCSSPageRule.prototype.style;\\n\\n/**\\n * @constructor\\n * @extends {CSSRule}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSImportRule\\n */\\nfunction CSSImportRule() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSImportRule-href\\n */\\nCSSImportRule.prototype.href;\\n\\n/**\\n * @type {MediaList}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSImportRule-media\\n */\\nCSSImportRule.prototype.media;\\n\\n/**\\n * @type {CSSStyleSheet}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSImportRule-styleSheet\\n */\\nCSSImportRule.prototype.styleSheet;\\n\\n/**\\n * @constructor\\n * @extends {CSSRule}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSCharsetRule\\n */\\nfunction CSSCharsetRule() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSCharsetRule-encoding\\n */\\nCSSCharsetRule.prototype.encoding;\\n\\n/**\\n * @constructor\\n * @extends {CSSRule}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSUnknownRule\\n */\\nfunction CSSUnknownRule() {}\\n\\n/**\\n * @constructor\\n * @extends {CSSProperties}\\n * @implements {IObject<(string|number), string>}\\n * @implements {IArrayLike}\\n * @implements {Iterable}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration\\n */\\nfunction CSSStyleDeclaration() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-cssText\\n */\\nCSSStyleDeclaration.prototype.cssText;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-length\\n */\\nCSSStyleDeclaration.prototype.length;\\n\\n/**\\n * @type {CSSRule}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-parentRule\\n */\\nCSSStyleDeclaration.prototype.parentRule;\\n\\n/**\\n * @param {string} propertyName\\n * @return {CSSValue}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-getPropertyCSSValue\\n */\\nCSSStyleDeclaration.prototype.getPropertyCSSValue = function(propertyName) {};\\n\\n/**\\n * @param {string} propertyName\\n * @return {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-getPropertyPriority\\n */\\nCSSStyleDeclaration.prototype.getPropertyPriority = function(propertyName) {};\\n\\n/**\\n * @param {string} propertyName\\n * @return {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-getPropertyValue\\n */\\nCSSStyleDeclaration.prototype.getPropertyValue = function(propertyName) {};\\n\\n/**\\n * @param {number} index\\n * @return {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-item\\n */\\nCSSStyleDeclaration.prototype.item = function(index) {};\\n\\n/**\\n * @param {string} propertyName\\n * @return {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-removeProperty\\n */\\nCSSStyleDeclaration.prototype.removeProperty = function(propertyName) {};\\n\\n/**\\n * @param {string} propertyName\\n * @param {string} value\\n * @param {string=} opt_priority\\n * @return {undefined}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-setProperty\\n */\\nCSSStyleDeclaration.prototype.setProperty = function(propertyName, value, opt_priority) {};\\n\\n// IE-specific\\n\\n/**\\n * @param {string} name\\n * @param {number=} opt_flags\\n * @return {string|number|boolean|null}\\n * @see http://msdn.microsoft.com/en-us/library/ms536429(VS.85).aspx\\n */\\nCSSStyleDeclaration.prototype.getAttribute = function(name, opt_flags) {};\\n\\n/**\\n * @param {string} name\\n * @return {string|number|boolean|null}\\n * @see http://msdn.microsoft.com/en-us/library/aa358797(VS.85).aspx\\n */\\nCSSStyleDeclaration.prototype.getExpression = function(name) {};\\n\\n/**\\n * @param {string} name\\n * @param {number=} opt_flags\\n * @return {boolean}\\n * @see http://msdn.microsoft.com/en-us/library/ms536696(VS.85).aspx\\n */\\nCSSStyleDeclaration.prototype.removeAttribute =\\n function(name, opt_flags) {};\\n\\n/**\\n * @param {string} name\\n * @return {boolean}\\n * @see http://msdn.microsoft.com/en-us/library/aa358798(VS.85).aspx\\n */\\nCSSStyleDeclaration.prototype.removeExpression = function(name) {};\\n\\n/**\\n * @deprecated\\n * @param {string} name\\n * @param {*} value\\n * @param {number=} opt_flags\\n * @see http://msdn.microsoft.com/en-us/library/ms536739(VS.85).aspx\\n * @return {undefined}\\n */\\nCSSStyleDeclaration.prototype.setAttribute = function(name, value, opt_flags) {};\\n\\n/**\\n * @param {string} name\\n * @param {string} expr\\n * @param {string=} opt_language\\n * @return {undefined}\\n * @see http://msdn.microsoft.com/en-us/library/ms531196(VS.85).aspx\\n */\\nCSSStyleDeclaration.prototype.setExpression =\\n function(name, expr, opt_language) {};\\n\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue\\n */\\nfunction CSSValue() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue-cssText\\n */\\nCSSValue.prototype.cssText;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue-cssValueType\\n */\\nCSSValue.prototype.cssValueType;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue-types\\n */\\nCSSValue.CSS_INHERIT = 0;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue-types\\n */\\nCSSValue.CSS_PRIMITIVE_VALUE = 1;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue-types\\n */\\nCSSValue.CSS_VALUE_LIST = 2;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue-types\\n */\\nCSSValue.CSS_CUSTOM = 3;\\n\\n/**\\n * @constructor\\n * @extends {CSSValue}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nfunction CSSPrimitiveValue() {}\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.prototype.primitiveType;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_UNKNOWN = 0;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_NUMBER = 1;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_PERCENTAGE = 2;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_EMS = 3;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_EXS = 4;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_PX = 5;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_CM = 6;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_MM = 7;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_IN = 8;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_PT = 9;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_PC = 10;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_DEG = 11;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_RAD = 12;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_GRAD = 13;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_MS = 14;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_S = 15;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_HZ = 16;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_KHZ = 17;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_DIMENSION = 18;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_STRING = 19;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_URI = 20;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_IDENT = 21;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_ATTR = 22;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_COUNTER = 23;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_RECT = 24;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue\\n */\\nCSSPrimitiveValue.CSS_RGBCOLOR = 25;\\n\\n/**\\n * @return {Counter}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue-getCounterValue\\n * @throws DOMException {@see DomException.INVALID_ACCESS_ERR}\\n */\\nCSSPrimitiveValue.prototype.getCounterValue = function() {};\\n\\n/**\\n * @param {number} unitType\\n * @return {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue-getFloatValue\\n * @throws DOMException {@see DomException.INVALID_ACCESS_ERR}\\n */\\nCSSPrimitiveValue.prototype.getFloatValue = function(unitType) {};\\n\\n/**\\n * @return {RGBColor}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue-getRGBColorValue\\n * @throws DOMException {@see DomException.INVALID_ACCESS_ERR}\\n */\\nCSSPrimitiveValue.prototype.getRGBColorValue = function() {};\\n\\n/**\\n * @return {Rect}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue-getRectValue\\n * @throws DOMException {@see DomException.INVALID_ACCESS_ERR}\\n */\\nCSSPrimitiveValue.prototype.getRectValue = function() {};\\n\\n/**\\n * @return {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue-getStringValue\\n * @throws DOMException {@see DomException.INVALID_ACCESS_ERR}\\n */\\nCSSPrimitiveValue.prototype.getStringValue = function() {};\\n\\n/**\\n * @param {number} unitType\\n * @param {number} floatValue\\n * @return {undefined}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue-setFloatValue\\n * @throws DOMException {@see DomException.INVALID_ACCESS_ERR},\\n * {@see DomException.NO_MODIFICATION_ALLOWED_ERR}\\n */\\nCSSPrimitiveValue.prototype.setFloatValue = function(unitType, floatValue) {};\\n\\n/**\\n * @param {number} stringType\\n * @param {string} stringValue\\n * @return {undefined}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue-setStringValue\\n * @throws DOMException {@see DomException.INVALID_ACCESS_ERR},\\n * {@see DomException.NO_MODIFICATION_ALLOWED_ERR}\\n */\\nCSSPrimitiveValue.prototype.setStringValue = function(stringType, stringValue) {};\\n\\n/**\\n * @constructor\\n * @extends {CSSValue}\\n * @implements {IArrayLike}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValueList\\n */\\nfunction CSSValueList() {}\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValueList-length\\n */\\nCSSValueList.prototype.length;\\n\\n/**\\n * @param {number} index\\n * @return {CSSValue}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValueList-item\\n */\\nCSSValueList.prototype.item = function(index) {};\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-RGBColor\\n */\\nfunction RGBColor() {}\\n\\n/**\\n * @type {CSSPrimitiveValue}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-RGBColor-red\\n */\\nRGBColor.prototype.red;\\n\\n/**\\n * @type {CSSPrimitiveValue}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-RGBColor-green\\n */\\nRGBColor.prototype.green;\\n\\n/**\\n * @type {CSSPrimitiveValue}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-RGBColor-blue\\n */\\nRGBColor.prototype.blue;\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-Rect\\n */\\nfunction Rect() {}\\n\\n/**\\n * @type {CSSPrimitiveValue}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-Rect-top\\n */\\nRect.prototype.top;\\n\\n/**\\n * @type {CSSPrimitiveValue}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-Rect-right\\n */\\nRect.prototype.right;\\n\\n/**\\n * @type {CSSPrimitiveValue}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-Rect-bottom\\n */\\nRect.prototype.bottom;\\n\\n/**\\n * @type {CSSPrimitiveValue}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-Rect-left\\n */\\nRect.prototype.left;\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-Counter\\n */\\nfunction Counter() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-Counter-identifier\\n */\\nCounter.prototype.identifier;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-Counter-listStyle\\n */\\nCounter.prototype.listStyle;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-Counter-separator\\n */\\nCounter.prototype.separator;\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-ViewCSS\\n */\\nfunction ViewCSS() {}\\n\\n/**\\n * @param {Element} elt\\n * @param {?string=} opt_pseudoElt This argument is required according to the\\n * CSS2 specification, but optional in all major browsers. See the note at\\n * https://developer.mozilla.org/en-US/docs/Web/API/Window.getComputedStyle\\n * @return {?CSSStyleDeclaration}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSview-getComputedStyle\\n * @see https://bugzilla.mozilla.org/show_bug.cgi?id=548397\\n */\\nViewCSS.prototype.getComputedStyle = function(elt, opt_pseudoElt) {};\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css."; +a.a+="html#CSS-DocumentCSS\\n */\\nfunction DocumentCSS() {}\\n\\n/**\\n * @param {Element} elt\\n * @param {string} pseudoElt\\n * @return {CSSStyleDeclaration}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-DocumentCSS-getOverrideStyle\\n */\\nDocumentCSS.prototype.getOverrideStyle = function(elt, pseudoElt) {};\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-DOMImplementationCSS\\n */\\nfunction DOMImplementationCSS() {}\\n\\n/**\\n * @param {string} title\\n * @param {string} media\\n * @return {CSSStyleSheet}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-DOMImplementationCSS-createCSSStyleSheet\\n * @throws DOMException {@see DomException.SYNTAX_ERR}\\n */\\nDOMImplementationCSS.prototype.createCSSStyleSheet = function(title, media) {};\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-ElementCSSInlineStyle\\n */\\nfunction ElementCSSInlineStyle() {}\\n\\n/**\\n * @type {CSSStyleDeclaration}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-ElementCSSInlineStyle-style\\n */\\nElementCSSInlineStyle.prototype.style;\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties\\n */\\nfunction CSSProperties() {}\\n\\n// CSS 2 properties\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-azimuth\\n */\\nCSSProperties.prototype.azimuth;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-background\\n */\\nCSSProperties.prototype.background;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-backgroundAttachment\\n */\\nCSSProperties.prototype.backgroundAttachment;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-backgroundColor\\n */\\nCSSProperties.prototype.backgroundColor;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-backgroundImage\\n */\\nCSSProperties.prototype.backgroundImage;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-backgroundPosition\\n */\\nCSSProperties.prototype.backgroundPosition;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-backgroundRepeat\\n */\\nCSSProperties.prototype.backgroundRepeat;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/css3-background/#the-background-size\\n */\\nCSSProperties.prototype.backgroundSize;\\n\\n/**\\n * @implicitCast\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-border\\n */\\nCSSProperties.prototype.border;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderCollapse\\n */\\nCSSProperties.prototype.borderCollapse;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderColor\\n */\\nCSSProperties.prototype.borderColor;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderSpacing\\n */\\nCSSProperties.prototype.borderSpacing;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue-borderStyle\\n */\\nCSSProperties.prototype.borderStyle;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderTop\\n */\\nCSSProperties.prototype.borderTop;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderRight\\n */\\nCSSProperties.prototype.borderRight;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderBottom\\n */\\nCSSProperties.prototype.borderBottom;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderLeft\\n */\\nCSSProperties.prototype.borderLeft;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderTopColor\\n */\\nCSSProperties.prototype.borderTopColor;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderRightColor\\n */\\nCSSProperties.prototype.borderRightColor;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderBottomColor\\n */\\nCSSProperties.prototype.borderBottomColor;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderLeftColor\\n */\\nCSSProperties.prototype.borderLeftColor;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderTopStyle\\n */\\nCSSProperties.prototype.borderTopStyle;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderRightStyle\\n */\\nCSSProperties.prototype.borderRightStyle;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderBottomStyle\\n */\\nCSSProperties.prototype.borderBottomStyle;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderLeftStyle\\n */\\nCSSProperties.prototype.borderLeftStyle;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderTopWidth\\n */\\nCSSProperties.prototype.borderTopWidth;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderRightWidth\\n */\\nCSSProperties.prototype.borderRightWidth;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderBottomWidth\\n */\\nCSSProperties.prototype.borderBottomWidth;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderLeftWidth\\n */\\nCSSProperties.prototype.borderLeftWidth;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderWidth\\n */\\nCSSProperties.prototype.borderWidth;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/css3-background/#the-border-radius\\n */\\nCSSProperties.prototype.borderRadius;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/css3-background/#the-border-radius\\n */\\nCSSProperties.prototype.borderBottomLeftRadius;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/css3-background/#the-border-radius\\n */\\nCSSProperties.prototype.borderBottomRightRadius;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/css3-background/#the-border-radius\\n */\\nCSSProperties.prototype.borderTopLeftRadius;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/css3-background/#the-border-radius\\n */\\nCSSProperties.prototype.borderTopRightRadius;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/css3-background/#the-border-image-source\\n */\\nCSSProperties.prototype.borderImageSource;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/css3-background/#the-border-image-slice\\n */\\nCSSProperties.prototype.borderImageSlice;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/css3-background/#the-border-image-width\\n */\\nCSSProperties.prototype.borderImageWidth;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/css3-background/#the-border-image-outset\\n */\\nCSSProperties.prototype.borderImageOutset;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/css3-background/#the-border-image-repeat\\n */\\nCSSProperties.prototype.borderImageRepeat;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/css3-background/#the-border-image\\n */\\nCSSProperties.prototype.borderImage;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/1998/REC-CSS2-19980512/visuren.html#propdef-bottom\\n */\\nCSSProperties.prototype.bottom;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-captionSide\\n */\\nCSSProperties.prototype.captionSide;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-clear\\n */\\nCSSProperties.prototype.clear;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-clip\\n */\\nCSSProperties.prototype.clip;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-color\\n */\\nCSSProperties.prototype.color;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-content\\n */\\nCSSProperties.prototype.content;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-counterIncrement\\n */\\nCSSProperties.prototype.counterIncrement;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-counterReset\\n */\\nCSSProperties.prototype.counterReset;\\n\\n/**\\n * This is not an official part of the W3C spec. In practice, this is a settable\\n * property that works cross-browser. It is used in goog.dom.setProperties() and\\n * needs to be extern'd so the --disambiguate_properties JS compiler pass works.\\n * @type {string}\\n */\\nCSSProperties.prototype.cssText;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-cue\\n */\\nCSSProperties.prototype.cue;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-cueAfter\\n */\\nCSSProperties.prototype.cueAfter;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-cueBefore\\n */\\nCSSProperties.prototype.cueBefore;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-cursor\\n */\\nCSSProperties.prototype.cursor;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-direction\\n */\\nCSSProperties.prototype.direction;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-display\\n */\\nCSSProperties.prototype.display;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-elevation\\n */\\nCSSProperties.prototype.elevation;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-emptyCells\\n */\\nCSSProperties.prototype.emptyCells;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-cssFloat\\n */\\nCSSProperties.prototype.cssFloat;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-font\\n */\\nCSSProperties.prototype.font;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-fontFamily\\n */\\nCSSProperties.prototype.fontFamily;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-fontSize\\n */\\nCSSProperties.prototype.fontSize;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-fontSizeAdjust\\n */\\nCSSProperties.prototype.fontSizeAdjust;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-fontStretch\\n */\\nCSSProperties.prototype.fontStretch;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-fontStyle\\n */\\nCSSProperties.prototype.fontStyle;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-fontVariant\\n */\\nCSSProperties.prototype.fontVariant;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-fontWeight\\n */\\nCSSProperties.prototype.fontWeight;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-height\\n */\\nCSSProperties.prototype.height;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/1998/REC-CSS2-19980512/visuren.html#propdef-left\\n */\\nCSSProperties.prototype.left;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-letterSpacing\\n */\\nCSSProperties.prototype.letterSpacing;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-lineHeight\\n */\\nCSSProperties.prototype.lineHeight;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-listStyle\\n */\\nCSSProperties.prototype.listStyle;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-listStyleImage\\n */\\nCSSProperties.prototype.listStyleImage;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-listStylePosition\\n */\\nCSSProperties.prototype.listStylePosition;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-listStyleType\\n */\\nCSSProperties.prototype.listStyleType;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-margin\\n */\\nCSSProperties.prototype.margin;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-marginTop\\n */\\nCSSProperties.prototype.marginTop;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-marginRight\\n */\\nCSSProperties.prototype.marginRight;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-marginBottom\\n */\\nCSSProperties.prototype.marginBottom;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-marginLeft\\n */\\nCSSProperties.prototype.marginLeft;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-markerOffset\\n */\\nCSSProperties.prototype.markerOffset;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-marks\\n */\\nCSSProperties.prototype.marks;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-maxHeight\\n */\\nCSSProperties.prototype.maxHeight;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-maxWidth\\n */\\nCSSProperties.prototype.maxWidth;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-minHeight\\n */\\nCSSProperties.prototype.minHeight;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-minWidth\\n */\\nCSSProperties.prototype.minWidth;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-orphans\\n */\\nCSSProperties.prototype.orphans;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-outline\\n */\\nCSSProperties.prototype.outline;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-outlineColor\\n */\\nCSSProperties.prototype.outlineColor;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-outlineStyle\\n */\\nCSSProperties.prototype.outlineStyle;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-outlineWidth\\n */\\nCSSProperties.prototype.outlineWidth;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-overflow\\n */\\nCSSProperties.prototype.overflow;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-padding\\n */\\nCSSProperties.prototype.padding;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-paddingTop\\n */\\nCSSProperties.prototype.paddingTop;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-paddingRight\\n */\\nCSSProperties.prototype.paddingRight;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-paddingBottom"; +a.a+="\\n */\\nCSSProperties.prototype.paddingBottom;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-paddingLeft\\n */\\nCSSProperties.prototype.paddingLeft;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-page\\n */\\nCSSProperties.prototype.page;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-pageBreakAfter\\n */\\nCSSProperties.prototype.pageBreakAfter;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-pageBreakBefore\\n */\\nCSSProperties.prototype.pageBreakBefore;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-pageBreakInside\\n */\\nCSSProperties.prototype.pageBreakInside;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-pause\\n */\\nCSSProperties.prototype.pause;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-pauseAfter\\n */\\nCSSProperties.prototype.pauseAfter;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-pauseBefore\\n */\\nCSSProperties.prototype.pauseBefore;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-pitch\\n */\\nCSSProperties.prototype.pitch;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-pitchRange\\n */\\nCSSProperties.prototype.pitchRange;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-playDuring\\n */\\nCSSProperties.prototype.playDuring;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-position\\n */\\nCSSProperties.prototype.position;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-quotes\\n */\\nCSSProperties.prototype.quotes;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/css3-ui/#resize\\n */\\nCSSProperties.prototype.resize;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-richness\\n */\\nCSSProperties.prototype.richness;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/1998/REC-CSS2-19980512/visuren.html#propdef-right\\n */\\nCSSProperties.prototype.right;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-size\\n */\\nCSSProperties.prototype.size;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-speak\\n */\\nCSSProperties.prototype.speak;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-speakHeader\\n */\\nCSSProperties.prototype.speakHeader;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-speakNumeral\\n */\\nCSSProperties.prototype.speakNumeral;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-speakPunctuation\\n */\\nCSSProperties.prototype.speakPunctuation;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-speechRate\\n */\\nCSSProperties.prototype.speechRate;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-stress\\n */\\nCSSProperties.prototype.stress;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-tableLayout\\n */\\nCSSProperties.prototype.tableLayout;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-textAlign\\n */\\nCSSProperties.prototype.textAlign;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-textDecoration\\n */\\nCSSProperties.prototype.textDecoration;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-textIndent\\n */\\nCSSProperties.prototype.textIndent;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-textShadow\\n */\\nCSSProperties.prototype.textShadow;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-textTransform\\n */\\nCSSProperties.prototype.textTransform;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/1998/REC-CSS2-19980512/visuren.html#propdef-top\\n */\\nCSSProperties.prototype.top;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-unicodeBidi\\n */\\nCSSProperties.prototype.unicodeBidi;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-verticalAlign\\n */\\nCSSProperties.prototype.verticalAlign;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-visibility\\n */\\nCSSProperties.prototype.visibility;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-voiceFamily\\n */\\nCSSProperties.prototype.voiceFamily;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-volume\\n */\\nCSSProperties.prototype.volume;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-whiteSpace\\n */\\nCSSProperties.prototype.whiteSpace;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-widows\\n */\\nCSSProperties.prototype.widows;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-width\\n */\\nCSSProperties.prototype.width;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-wordSpacing\\n */\\nCSSProperties.prototype.wordSpacing;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-wordWrap\\n */\\nCSSProperties.prototype.wordWrap;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-zIndex\\n */\\nCSSProperties.prototype.zIndex;\\n\\n// CSS 3 properties\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/css3-background/#box-shadow\\n */\\nCSSProperties.prototype.boxShadow;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/css3-ui/#box-sizing\\n */\\nCSSProperties.prototype.boxSizing;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/css3-color/#transparency\\n */\\nCSSProperties.prototype.opacity;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/css3-ui/#text-overflow\\n */\\nCSSProperties.prototype.textOverflow;\\n\\n// CSS 3 animations\\n\\n/**\\n * @type {string|number}\\n * @see https://www.w3.org/TR/css-animations-1/#animation\\n */\\nCSSProperties.prototype.animation;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/css-animations-1/#animation-delay\\n */\\nCSSProperties.prototype.animationDelay;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/css-animations-1/#animation-direction\\n */\\nCSSProperties.prototype.animationDirection;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/css-animations-1/#animation-duration\\n */\\nCSSProperties.prototype.animationDuration;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/css-animations-1/#animation-fill-mode\\n */\\nCSSProperties.prototype.animationFillMode;\\n\\n/**\\n * @type {string|number}\\n * @see https://www.w3.org/TR/css-animations-1/#animation-iteration-count\\n */\\nCSSProperties.prototype.animationIterationCount;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/css-animations-1/#animation-name\\n */\\nCSSProperties.prototype.animationName;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/css-animations-1/#animation-play-state\\n */\\nCSSProperties.prototype.animationPlayState;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/css-animations-1/#animation-timing-function\\n */\\nCSSProperties.prototype.animationTimingFunction;\\n\\n// CSS 3 transforms\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/css3-2d-transforms/#backface-visibility-property\\n */\\nCSSProperties.prototype.backfaceVisibility;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/css3-2d-transforms/#perspective\\n */\\nCSSProperties.prototype.perspective;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/css3-2d-transforms/#perspective-origin\\n */\\nCSSProperties.prototype.perspectiveOrigin;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/css3-2d-transforms/#effects\\n */\\nCSSProperties.prototype.transform;\\n\\n/**\\n * @type {string|number}\\n * @see http://www.w3.org/TR/css3-2d-transforms/#transform-origin\\n */\\nCSSProperties.prototype.transformOrigin;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/css3-2d-transforms/#transform-style\\n */\\nCSSProperties.prototype.transformStyle;\\n\\n// CSS 3 transitions\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/css3-transitions/#transition\\n */\\nCSSProperties.prototype.transition;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/css3-transitions/#transition-delay\\n */\\nCSSProperties.prototype.transitionDelay;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/css3-transitions/#transition-duration\\n */\\nCSSProperties.prototype.transitionDuration;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/css3-transitions/#transition-property-property\\n */\\nCSSProperties.prototype.transitionProperty;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/css3-transitions/#transition-timing-function\\n */\\nCSSProperties.prototype.transitionTimingFunction;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/SVG11/interact.html#PointerEventsProperty\\n */\\nCSSProperties.prototype.pointerEvents;\\n\\n\\n// CSS Flexbox 1\\n\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/css-flexbox-1/#align-content-property\\n */\\nCSSProperties.prototype.alignContent;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/css-flexbox-1/#align-items-property\\n */\\nCSSProperties.prototype.alignItems;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/css-flexbox-1/#align-items-property\\n */\\nCSSProperties.prototype.alignSelf;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/css-flexbox-1/#flex-property\\n */\\nCSSProperties.prototype.flex;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/css-flexbox-1/#flex-basis-property\\n */\\nCSSProperties.prototype.flexBasis;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/css-flexbox-1/#flex-direction-property\\n */\\nCSSProperties.prototype.flexDirection;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/css-flexbox-1/#flex-flow-property\\n */\\nCSSProperties.prototype.flexFlow;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/css-flexbox-1/#flex-grow-property\\n */\\nCSSProperties.prototype.flexGrow;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/css-flexbox-1/#flex-shrink-property\\n */\\nCSSProperties.prototype.flexShrink;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/css-flexbox-1/#flex-wrap-property\\n */\\nCSSProperties.prototype.flexWrap;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/css-flexbox-1/#justify-content-property\\n */\\nCSSProperties.prototype.justifyContent;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/css-flexbox-1/#order-property\\n */\\nCSSProperties.prototype.order;\\n\\n// Externs for CSS Will Change Module Level 1\\n// http://www.w3.org/TR/css-will-change/\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/css-will-change-1/#will-change\\n */\\nCSSProperties.prototype.willChange;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/css-ui-4/#propdef-user-select\\n */\\nCSSProperties.prototype.userSelect;\\n\\n/**\\n * TODO(dbeam): Put this in separate file named w3c_cssom.js.\\n * Externs for the CSSOM View Module.\\n * @see http://www.w3.org/TR/cssom-view/\\n */\\n\\n// http://www.w3.org/TR/cssom-view/#extensions-to-the-window-interface\\n\\n/**\\n * @param {string} media_query_list\\n * @return {!MediaQueryList}\\n * @see http://www.w3.org/TR/cssom-view/#dom-window-matchmedia\\n */\\nWindow.prototype.matchMedia = function(media_query_list) {};\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-window-innerwidth\\n */\\nWindow.prototype.innerWidth;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-window-innerheight\\n */\\nWindow.prototype.innerHeight;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-window-scrollx\\n */\\nWindow.prototype.scrollX;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-window-pagexoffset\\n */\\nWindow.prototype.pageXOffset;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-window-scrolly\\n */\\nWindow.prototype.scrollY;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-window-pageyoffset\\n */\\nWindow.prototype.pageYOffset;\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @see http://www.w3.org/TR/cssom-view/#dom-window-scroll\\n * @return {undefined}\\n */\\nWindow.prototype.scroll = function(x, y) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @see http://www.w3.org/TR/cssom-view/#dom-window-scrollto\\n * @return {undefined}\\n */\\nWindow.prototype.scrollTo = function(x, y) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @see http://www.w3.org/TR/cssom-view/#dom-window-scrollby\\n * @return {undefined}\\n */\\nWindow.prototype.scrollBy = function(x, y) {};\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-window-screenx\\n */\\nWindow.prototype.screenX;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-window-screeny\\n */\\nWindow.prototype.screenY;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-window-outerwidth\\n */\\nWindow.prototype.outerWidth;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-window-outerheight\\n */\\nWindow.prototype.outerHeight;\\n\\n/**\\n * @constructor\\n * @implements {EventTarget}\\n * @see http://www.w3.org/TR/cssom-view/#mediaquerylist\\n */\\nfunction MediaQueryList() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/cssom-view/#dom-mediaquerylist-media\\n */\\nMediaQueryList.prototype.media;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/cssom-view/#dom-mediaquerylist-matches\\n */\\nMediaQueryList.prototype.matches;\\n\\n/**\\n * @param {MediaQueryListListener} listener\\n * @see http://www.w3.org/TR/cssom-view/#dom-mediaquerylist-addlistener\\n * @return {undefined}\\n */\\nMediaQueryList.prototype.addListener = function(listener) {};\\n\\n/**\\n * @param {MediaQueryListListener} listener\\n * @see http://www.w3.org/TR/cssom-view/#dom-mediaquerylist-removelistener\\n * @return {undefined}\\n */\\nMediaQueryList.prototype.removeListener = function(listener) {};\\n\\n/** @override Not available in some browsers; use addListener instead. */\\nMediaQueryList.prototype.addEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override Not available in old browsers; use removeListener instead. */\\nMediaQueryList.prototype.removeEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nMediaQueryList.prototype.dispatchEvent = function(evt) {};\\n\\n/**\\n * @typedef {(function(!MediaQueryList) : void)}\\n * @see http://www.w3.org/TR/cssom-view/#mediaquerylistlistener\\n */\\nvar MediaQueryListListener;\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/cssom-view/#screen\\n */\\nfunction Screen() {}\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-screen-availwidth\\n */\\nScreen.prototype.availWidth;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-screen-availheight\\n */\\nScreen.prototype.availHeight;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-screen-width\\n */\\nScreen.prototype.width;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-screen-height\\n */\\nScreen.prototype.height;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-screen-colordepth\\n */\\nScreen.prototype.colorDepth;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-screen-pixeldepth\\n */\\nScreen.prototype.pixelDepth;\\n\\n\\n// http://www.w3.org/TR/cssom-view"; +a.a+="/#extensions-to-the-document-interface\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @return {?Element}\\n * @see http://www.w3.org/TR/cssom-view/#dom-document-elementfrompoint\\n */\\nDocument.prototype.elementFromPoint = function(x, y) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @return {!IArrayLike}\\n * @see http://www.w3.org/TR/cssom-view/#dom-document-elementsfrompoint\\n */\\nDocument.prototype.elementsFromPoint = function(x, y) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @return {CaretPosition}\\n * @see http://www.w3.org/TR/cssom-view/#dom-document-caretpositionfrompoint\\n */\\nDocument.prototype.caretPositionFromPoint = function(x, y) {};\\n\\n/**\\n * @type {Element}\\n * @see http://dev.w3.org/csswg/cssom-view/#dom-document-scrollingelement\\n */\\nDocument.prototype.scrollingElement;\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/cssom-view/#caretposition\\n */\\nfunction CaretPosition() {}\\n\\n/**\\n * @type {Node}\\n * @see http://www.w3.org/TR/cssom-view/#dom-caretposition-offsetnode\\n */\\nCaretPosition.prototype.offsetNode;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-caretposition-offset\\n */\\nCaretPosition.prototype.offset;\\n\\n\\n// http://www.w3.org/TR/cssom-view/#extensions-to-the-element-interface\\n\\n/**\\n * @return {!ClientRectList}\\n * @see http://www.w3.org/TR/cssom-view/#dom-element-getclientrects\\n */\\nElement.prototype.getClientRects = function() {};\\n\\n/**\\n * @return {!DOMRect}\\n * @see http://www.w3.org/TR/cssom-view/#dom-element-getboundingclientrect\\n */\\nElement.prototype.getBoundingClientRect = function() {};\\n\\n/**\\n * @param {(boolean|{behavior: string, block: string})=} opt_top\\n * @see http://www.w3.org/TR/cssom-view/#dom-element-scrollintoview\\n * @return {undefined}\\n */\\nElement.prototype.scrollIntoView = function(opt_top) {};\\n\\n/**\\n * @param {number|{\\n * left: (number|undefined),\\n * top: (number|undefined),\\n * behavior: (string|undefined),\\n * }} scrollToOptionsOrX\\n * @param {number=} opt_y\\n * @see https://www.w3.org/TR/cssom-view/#extension-to-the-element-interface\\n * @return {undefined}\\n */\\nElement.prototype.scrollTo = function(scrollToOptionsOrX, opt_y) {};\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-element-scrolltop\\n */\\nElement.prototype.scrollTop;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-element-scrollleft\\n */\\nElement.prototype.scrollLeft;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-element-scrollwidth\\n */\\nElement.prototype.scrollWidth;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-element-scrollheight\\n */\\nElement.prototype.scrollHeight;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-element-clienttop\\n */\\nElement.prototype.clientTop;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-element-clientleft\\n */\\nElement.prototype.clientLeft;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-element-clientwidth\\n */\\nElement.prototype.clientWidth;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-element-clientheight\\n */\\nElement.prototype.clientHeight;\\n\\n// http://www.w3.org/TR/cssom-view/#extensions-to-the-htmlelement-interface\\n\\n/**\\n * @type {Element}\\n * @see http://www.w3.org/TR/cssom-view/#dom-htmlelement-offsetparent\\n */\\nHTMLElement.prototype.offsetParent;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-htmlelement-offsettop\\n */\\nHTMLElement.prototype.offsetTop;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-htmlelement-offsetleft\\n */\\nHTMLElement.prototype.offsetLeft;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-htmlelement-offsetwidth\\n */\\nHTMLElement.prototype.offsetWidth;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-htmlelement-offsetheight\\n */\\nHTMLElement.prototype.offsetHeight;\\n\\n\\n// http://www.w3.org/TR/cssom-view/#extensions-to-the-range-interface\\n\\n/**\\n * @return {!ClientRectList}\\n * @see http://www.w3.org/TR/cssom-view/#dom-range-getclientrects\\n */\\nRange.prototype.getClientRects = function() {};\\n\\n/**\\n * @return {!DOMRect}\\n * @see http://www.w3.org/TR/cssom-view/#dom-range-getboundingclientrect\\n */\\nRange.prototype.getBoundingClientRect = function() {};\\n\\n\\n// http://www.w3.org/TR/cssom-view/#extensions-to-the-mouseevent-interface\\n\\n// MouseEvent: screen{X,Y} and client{X,Y} are in DOM Level 2/3 Event as well,\\n// so it seems like a specification issue. I've emailed www-style@w3.org in\\n// hopes of resolving the conflict, but in the mean time they can live here\\n// (http://lists.w3.org/Archives/Public/www-style/2012May/0039.html).\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-mouseevent-screenx\\n */\\n//MouseEvent.prototype.screenX;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-mouseevent-screeny\\n */\\n//MouseEvent.prototype.screenY;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-mouseevent-pagex\\n */\\nMouseEvent.prototype.pageX;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-mouseevent-pagey\\n */\\nMouseEvent.prototype.pageY;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-mouseevent-clientx\\n */\\n//MouseEvent.prototype.clientX;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-mouseevent-clienty\\n */\\n//MouseEvent.prototype.clientY;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-mouseevent-x\\n */\\nMouseEvent.prototype.x;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-mouseevent-y\\n */\\nMouseEvent.prototype.y;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-mouseevent-offsetx\\n */\\nMouseEvent.prototype.offsetX;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-mouseevent-offsety\\n */\\nMouseEvent.prototype.offsetY;\\n\\n\\n// http://www.w3.org/TR/cssom-view/#rectangles\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/cssom-view/#the-clientrectlist-interface\\n * @implements {IArrayLike}\\n */\\nfunction ClientRectList() {}\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-clientrectlist-length\\n */\\nClientRectList.prototype.length;\\n\\n/**\\n * @param {number} index\\n * @return {?DOMRect}\\n * @see http://www.w3.org/TR/cssom-view/#dom-clientrectlist-item\\n */\\nClientRectList.prototype.item = function(index) {};\\n\\n/**\\n * @constructor\\n * http://www.w3.org/TR/css3-conditional/#CSS-interface\\n */\\nfunction CSSInterface() {}\\n\\n/**\\n * @param {string} ident\\n * @return {string}\\n * @see http://www.w3.org/TR/cssom/#the-css.escape()-method\\n * @throws DOMException {@see DOMException.INVALID_CHARACTER_ERR}\\n */\\nCSSInterface.prototype.escape = function(ident) {};\\n\\n/**\\n * @param {string} property\\n * @param {string=} opt_value\\n * @return {boolean}\\n */\\nCSSInterface.prototype.supports = function(property, opt_value) {};\\n\\n/**\\n * TODO(nicksantos): This suppress tag probably isn't needed, and\\n * should be removed.\\n * @suppress {duplicate}\\n * @type {CSSInterface}\\n */\\nvar CSS;\\n\\n/** @type {CSSInterface} */\\nWindow.prototype.CSS;\\n\\n// http://dev.w3.org/csswg/css-font-loading/\\n\\n/**\\n * Set of possible string values: 'error', 'loaded', 'loading', 'unloaded'.\\n * @typedef {string}\\n * @see http://dev.w3.org/csswg/css-font-loading/#enumdef-fontfaceloadstatus\\n */\\nvar FontFaceLoadStatus;\\n\\n/**\\n * @typedef {{\\n * style: (string|undefined),\\n * weight: (string|undefined),\\n * stretch: (string|undefined),\\n * unicodeRange: (string|undefined),\\n * variant: (string|undefined),\\n * featureSettings: (string|undefined)\\n * }}\\n * @see http://dev.w3.org/csswg/css-font-loading/#dictdef-fontfacedescriptors\\n */\\nvar FontFaceDescriptors;\\n\\n/**\\n * @constructor\\n * @param {string} fontFamily\\n * @param {(string|ArrayBuffer|ArrayBufferView)} source\\n * @param {!FontFaceDescriptors=} opt_descriptors\\n * @see http://dev.w3.org/csswg/css-font-loading/#font-face-constructor\\n */\\nfunction FontFace(fontFamily, source, opt_descriptors) {}\\n\\n/**\\n * @type {string}\\n * @see http://dev.w3.org/csswg/css-font-loading/#dom-fontface-family\\n */\\nFontFace.prototype.family;\\n\\n/**\\n * @type {string}\\n * @see http://dev.w3.org/csswg/css-font-loading/#dom-fontface-style\\n */\\nFontFace.prototype.style;\\n\\n/**\\n * @type {string}\\n * @see http://dev.w3.org/csswg/css-font-loading/#dom-fontface-weight\\n */\\nFontFace.prototype.weight;\\n\\n/**\\n * @type {string}\\n * @see http://dev.w3.org/csswg/css-font-loading/#dom-fontface-stretch\\n */\\nFontFace.prototype.stretch;\\n\\n/**\\n * @type {string}\\n * @see http://dev.w3.org/csswg/css-font-loading/#dom-fontface-unicoderange\\n */\\nFontFace.prototype.unicodeRange;\\n\\n/**\\n * @type {string}\\n * @see http://dev.w3.org/csswg/css-font-loading/#dom-fontface-variant\\n */\\nFontFace.prototype.variant;\\n\\n/**\\n * @type {string}\\n * @see http://dev.w3.org/csswg/css-font-loading/#dom-fontface-featuresettings\\n */\\nFontFace.prototype.featureSettings;\\n\\n/**\\n * @type {FontFaceLoadStatus}\\n * @see http://dev.w3.org/csswg/css-font-loading/#dom-fontface-status\\n */\\nFontFace.prototype.status;\\n\\n/**\\n * @return {!Promise}\\n * @see http://dev.w3.org/csswg/css-font-loading/#font-face-load\\n */\\nFontFace.prototype.load = function() {};\\n\\n/**\\n * Set of possible string values: 'loaded', 'loading'.\\n * @typedef {string}\\n * @see http://dev.w3.org/csswg/css-font-loading/#enumdef-fontfacesetloadstatus\\n */\\nvar FontFaceSetLoadStatus;\\n\\n/**\\n * @interface\\n * @extends {EventTarget}\\n * @see http://dev.w3.org/csswg/css-font-loading/#FontFaceSet-interface\\n */\\nfunction FontFaceSet() {}\\n\\n// Event handlers\\n// http://dev.w3.org/csswg/css-font-loading/#FontFaceSet-events\\n\\n/** @type {?function (Event)} */ FontFaceSet.prototype.onloading;\\n/** @type {?function (Event)} */ FontFaceSet.prototype.onloadingdone;\\n/** @type {?function (Event)} */ FontFaceSet.prototype.onloadingerror;\\n\\n/**\\n * @param {!FontFace} value\\n * @see http://dev.w3.org/csswg/css-font-loading/#dom-fontfaceset-add\\n * @return {undefined}\\n */\\nFontFaceSet.prototype.add = function(value) {};\\n\\n/**\\n * @see http://dev.w3.org/csswg/css-font-loading/#dom-fontfaceset-clear\\n * @return {undefined}\\n */\\nFontFaceSet.prototype.clear = function() {};\\n\\n/**\\n * @param {!FontFace} value\\n * @see http://dev.w3.org/csswg/css-font-loading/#dom-fontfaceset-delete\\n * @return {undefined}\\n */\\nFontFaceSet.prototype.delete = function(value) {};\\n\\n/**\\n * @param {!FontFace} font\\n * @return {boolean}\\n * @see http://dev.w3.org/csswg/css-font-loading/#dom-fontfaceset-has\\n */\\nFontFaceSet.prototype.has = function(font) {};\\n\\n/**\\n * @param {function(!FontFace, number, !FontFaceSet)} callback\\n * @param {Object|undefined=} opt_selfObj\\n * see http://dev.w3.org/csswg/css-font-loading/#dom-fontfaceset-foreach\\n * @return {undefined}\\n */\\nFontFaceSet.prototype.forEach = function(callback, opt_selfObj) {};\\n\\n/**\\n * @param {string} font\\n * @param {string=} opt_text\\n * @return {!Promise>}\\n * @see http://dev.w3.org/csswg/css-font-loading/#dom-fontfaceset-load\\n */\\nFontFaceSet.prototype.load = function(font, opt_text) {};\\n\\n/**\\n * @param {string} font\\n * @param {string=} opt_text\\n * @return {boolean}\\n * @see http://dev.w3.org/csswg/css-font-loading/#dom-fontfaceset-check\\n */\\nFontFaceSet.prototype.check = function(font, opt_text) {};\\n\\n/**\\n * @type {!Promise}\\n * @see http://dev.w3.org/csswg/css-font-loading/#dom-fontfaceset-ready\\n */\\nFontFaceSet.prototype.ready;\\n\\n/**\\n * @type {FontFaceSetLoadStatus}\\n * @see http://dev.w3.org/csswg/css-font-loading/#dom-fontfaceset-status\\n */\\nFontFaceSet.prototype.status;\\n\\n/**\\n * @constructor\\n * @param {string} type\\n * @param {{\\n * animationName: (string|undefined),\\n * elapsedTime: (number|undefined),\\n * pseudoElement: (string|undefined)\\n * }=} opt_animationEventInitDict\\n * @extends {Event}\\n * @see https://drafts.csswg.org/css-animations/#interface-animationevent\\n */\\nfunction AnimationEvent(type, opt_animationEventInitDict) {};\\n\\n/**\\n * @type {string}\\n * @see https://drafts.csswg.org/css-animations/#dom-animationevent-animationname\\n */\\nAnimationEvent.prototype.animationName;\\n\\n/**\\n * @type {number}\\n * @see https://drafts.csswg.org/css-animations/#dom-animationevent-elapsedtime\\n */\\nAnimationEvent.prototype.elapsedTime;\\n\\n/**\\n * @type {string}\\n * @see https://drafts.csswg.org/css-animations/#dom-animationevent-pseudoelement\\n */\\nAnimationEvent.prototype.pseudoElement;\\n\\n/**\\n * @record\\n * @see http://dev.w3.org/csswg/css-animations/#csskeyframerule\\n */\\nfunction CSSKeyframeRule() {}\\n\\n/**\\n * @type {string}\\n * @see https://drafts.csswg.org/css-animations/#dom-csskeyframerule-keytext\\n */\\nCSSKeyframeRule.prototype.keyText;\\n\\n/**\\n * @type {!CSSStyleDeclaration}\\n * @see https://drafts.csswg.org/css-animations/#dom-csskeyframerule-style\\n */\\nCSSKeyframeRule.prototype.style;\\n\\n\\n/**\\n * @record\\n * @see http://dev.w3.org/csswg/css-animations/#csskeyframesrule\\n */\\nfunction CSSKeyframesRule() {}\\n\\n/**\\n * @see https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-name\\n * @type {string}\\n */\\nCSSKeyframesRule.prototype.name;\\n\\n/**\\n * @see https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-cssrules\\n * @type {!CSSRuleList}\\n */\\nCSSKeyframesRule.prototype.cssRules;\\n\\n/**\\n * @see https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-findrule\\n * @param {string} key The key text for the rule to find.\\n * @return {?CSSKeyframeRule}\\n */\\nCSSKeyframesRule.prototype.findRule = function(key) {};\\n\\n/**\\n * @see https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-appendrule\\n * @param {string} rule The text for the rule to append.\\n */\\nCSSKeyframesRule.prototype.appendRule = function(rule) {};\\n\\n/**\\n * @see https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-deleterule\\n * @param {string} key The key text for the rule to delete.\\n */\\nCSSKeyframesRule.prototype.deleteRule = function(key) {};\\n\",\"externs/gecko_css.js\":\"/*\\n * Copyright 2008 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for Gecko's custom CSS properties. Copied from:\\n * http://mxr.mozilla.org/mozilla2.0/source/dom/interfaces/css/nsIDOMCSS2Properties.idl\\n *\\n * @externs\\n * @author nicksantos@google.com (Nick Santos)\\n */\\n\\n\\n/** @type {string} */ CSSProperties.prototype.MozAppearance;\\n/** @type {string} */ CSSProperties.prototype.MozBackfaceVisibility;\\n/** @type {string} */ CSSProperties.prototype.MozBackgroundClip;\\n/** @type {string} */ CSSProperties.prototype.MozBackgroundInlinePolicy;\\n/** @type {string} */ CSSProperties.prototype.MozBackgroundOrigin;\\n/** @type {string} */ CSSProperties.prototype.MozBinding;\\n/** @type {string} */ CSSProperties.prototype.MozBorderBottomColors;\\n/** @type {string} */ CSSProperties.prototype.MozBorderEnd;\\n/** @type {string} */ CSSProperties.prototype.MozBorderEndColor;\\n/** @type {string} */ CSSProperties.prototype.MozBorderEndStyle;\\n/** @type {string} */ CSSProperties.prototype.MozBorderEndWidth;\\n/** @type {string} */ CSSProperties.prototype.MozBorderImage;\\n/** @type {string} */ CSSProperties.prototype.MozBorderLeftColors;\\n/** @type {string} */ CSSProperties.prototype.MozBorderRadius;\\n/** @type {string} */ CSSProperties.prototype.MozBorderRadiusTopleft;\\n/** @type {string} */ CSSProperties.prototype.MozBorderRadiusTopright;\\n/** @type {string} */ CSSProperties.prototype.MozBorderRadiusBottomleft;\\n/** @type {string} */ CSSProperties.prototype.MozBorderRadiusBottomright;\\n/** @type {string} */ CSSProperties.pr"; +a.a+='ototype.MozBorderRightColors;\\n/** @type {string} */ CSSProperties.prototype.MozBorderStart;\\n/** @type {string} */ CSSProperties.prototype.MozBorderStartColor;\\n/** @type {string} */ CSSProperties.prototype.MozBorderStartStyle;\\n/** @type {string} */ CSSProperties.prototype.MozBorderStartWidth;\\n/** @type {string} */ CSSProperties.prototype.MozBorderTopColors;\\n/** @type {string} */ CSSProperties.prototype.MozBoxAlign;\\n/** @type {string} */ CSSProperties.prototype.MozBoxDirection;\\n/** @type {string} */ CSSProperties.prototype.MozBoxFlex;\\n/** @type {string} */ CSSProperties.prototype.MozBoxOrdinalGroup;\\n/** @type {string} */ CSSProperties.prototype.MozBoxOrient;\\n/** @type {string} */ CSSProperties.prototype.MozBoxPack;\\n/** @type {string} */ CSSProperties.prototype.MozBoxSizing;\\n/** @type {string} */ CSSProperties.prototype.MozBoxShadow;\\n/** @type {string} */ CSSProperties.prototype.MozColumnCount;\\n/** @type {string} */ CSSProperties.prototype.MozColumnGap;\\n/** @type {string} */ CSSProperties.prototype.MozColumnRule;\\n/** @type {string} */ CSSProperties.prototype.MozColumnRuleColor;\\n/** @type {string} */ CSSProperties.prototype.MozColumnRuleStyle;\\n/** @type {string} */ CSSProperties.prototype.MozColumnRuleWidth;\\n/** @type {string} */ CSSProperties.prototype.MozColumnWidth;\\n/** @type {string} */ CSSProperties.prototype.MozFloatEdge;\\n/** @type {string} */ CSSProperties.prototype.MozFontFeatureSettings;\\n/** @type {string} */ CSSProperties.prototype.MozFontLanguageOverride;\\n/** @type {string} */ CSSProperties.prototype.MozForceBrokenImageIcon;\\n/** @type {string} */ CSSProperties.prototype.MozImageRegion;\\n/** @type {string} */ CSSProperties.prototype.MozMarginEnd;\\n/** @type {string} */ CSSProperties.prototype.MozMarginStart;\\n/** @type {number|string} */ CSSProperties.prototype.MozOpacity;\\n/** @type {string} */ CSSProperties.prototype.MozOutline;\\n/** @type {string} */ CSSProperties.prototype.MozOutlineColor;\\n/** @type {string} */ CSSProperties.prototype.MozOutlineOffset;\\n/** @type {string} */ CSSProperties.prototype.MozOutlineRadius;\\n/** @type {string} */ CSSProperties.prototype.MozOutlineRadiusBottomleft;\\n/** @type {string} */ CSSProperties.prototype.MozOutlineRadiusBottomright;\\n/** @type {string} */ CSSProperties.prototype.MozOutlineRadiusTopleft;\\n/** @type {string} */ CSSProperties.prototype.MozOutlineRadiusTopright;\\n/** @type {string} */ CSSProperties.prototype.MozOutlineStyle;\\n/** @type {string} */ CSSProperties.prototype.MozOutlineWidth;\\n/** @type {string} */ CSSProperties.prototype.MozPaddingEnd;\\n/** @type {string} */ CSSProperties.prototype.MozPaddingStart;\\n/** @type {string} */ CSSProperties.prototype.MozPerspective;\\n/** @type {string} */ CSSProperties.prototype.MozStackSizing;\\n/** @type {string} */ CSSProperties.prototype.MozTabSize;\\n/** @type {string} */ CSSProperties.prototype.MozTransform;\\n/** @type {string} */ CSSProperties.prototype.MozTransformOrigin;\\n/** @type {string} */ CSSProperties.prototype.MozTransition;\\n/** @type {string} */ CSSProperties.prototype.MozTransitionDelay;\\n/** @type {string} */ CSSProperties.prototype.MozTransitionDuration;\\n/** @type {string} */ CSSProperties.prototype.MozTransitionProperty;\\n/** @type {string} */ CSSProperties.prototype.MozTransitionTimingFunction;\\n/** @type {string} */ CSSProperties.prototype.MozUserFocus;\\n/** @type {string} */ CSSProperties.prototype.MozUserInput;\\n/** @type {string} */ CSSProperties.prototype.MozUserModify;\\n/** @type {string} */ CSSProperties.prototype.MozUserSelect;\\n/** @type {string} */ CSSProperties.prototype.MozWindowShadow;\\n\\n\\n// These are non-standard Gecko CSSOM properties on Window.prototype.screen.\\n\\n/**\\n * @type {number}\\n * @see https://developer.mozilla.org/En/DOM/window.screen.availTop\\n */\\nScreen.prototype.availTop;\\n\\n/**\\n * @type {number}\\n * @see https://developer.mozilla.org/En/DOM/window.screen.availLeft\\n */\\nScreen.prototype.availLeft;\\n\\n/**\\n * @type {number}\\n * @see https://developer.mozilla.org/En/DOM/window.screen.left\\n */\\nScreen.prototype.left;\\n\\n/**\\n * @type {number}\\n * @see https://developer.mozilla.org/En/DOM/window.screen.top\\n */\\nScreen.prototype.top;\\n","externs/ie_css.js":"/*\\n * Copyright 2008 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for IE\'s custom CSS properties, as defined here:\\n * http://msdn.microsoft.com/en-us/library/aa768661(VS.85).aspx\\n *\\n * This page is also useful for the IDL definitions:\\n * http://source.winehq.org/source/include/mshtml.idl\\n *\\n * @externs\\n * @author nicksantos@google.com\\n */\\n\\n/** @type {Element} */\\nStyleSheet.prototype.owningElement;\\n\\n/** @type {boolean} */\\nStyleSheet.prototype.readOnly;\\n\\n/** @type {StyleSheetList} */\\nStyleSheet.prototype.imports;\\n\\n/** @type {string} */\\nStyleSheet.prototype.id;\\n\\n/**\\n * @param {string} bstrURL\\n * @param {number} lIndex\\n * @return {number}\\n */\\nStyleSheet.prototype.addImport;\\n\\n/**\\n * @param {string} bstrSelector\\n * @param {string} bstrStyle\\n * @param {number=} opt_iIndex\\n * @return {number}\\n * @see http://msdn.microsoft.com/en-us/library/aa358796%28v=vs.85%29.aspx\\n */\\nStyleSheet.prototype.addRule;\\n\\n/**\\n * @param {number} lIndex\\n */\\nStyleSheet.prototype.removeImport;\\n\\n/**\\n * @param {number} lIndex\\n */\\nStyleSheet.prototype.removeRule;\\n\\n/** @type {string} */\\nStyleSheet.prototype.cssText;\\n\\n/** @type {CSSRuleList} */\\nStyleSheet.prototype.rules;\\n\\n// StyleSheet methods\\n\\n/**\\n * @param {string} propName\\n * @return {string}\\n * @see http://msdn.microsoft.com/en-us/library/aa358797(VS.85).aspx\\n */\\nStyleSheet.prototype.getExpression;\\n\\n/**\\n * @param {string} name\\n * @param {string} expression\\n * @return {undefined}\\n * @see http://msdn.microsoft.com/en-us/library/ms531196(VS.85).aspx\\n */\\nStyleSheet.prototype.setExpression;\\n\\n/**\\n * @param {string} expression\\n * @return {undefined}\\n * @see http://msdn.microsoft.com/en-us/library/aa358798(VS.85).aspx\\n */\\nStyleSheet.prototype.removeExpression;\\n\\n// IE-only CSS style names.\\n\\n/** @type {string} */ CSSProperties.prototype.backgroundPositionX;\\n\\n/** @type {string} */ CSSProperties.prototype.backgroundPositionY;\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ie/ms531081(v=vs.85).aspx\\n * NOTE: Left untyped to avoid conflict with caller.\\n */\\nCSSProperties.prototype.behavior;\\n\\n/**\\n * @type {string}\\n * @see http://msdn.microsoft.com/en-us/library/ms533883.aspx\\n */\\nCSSProperties.prototype.imeMode;\\n\\n/**\\n * @type {string}\\n * @see http://msdn.microsoft.com/en-us/library/ms534176(VS.85).aspx\\n */\\nCSSProperties.prototype.msInterpolationMode;\\n\\n/** @type {string} */ CSSProperties.prototype.overflowX;\\n\\n/** @type {string} */ CSSProperties.prototype.overflowY;\\n\\n/** @type {number} */ CSSProperties.prototype.pixelWidth;\\n\\n/** @type {number} */ CSSProperties.prototype.pixelHeight;\\n\\n/** @type {number} */ CSSProperties.prototype.pixelLeft;\\n\\n/** @type {number} */ CSSProperties.prototype.pixelTop;\\n\\n/** @type {string} */ CSSProperties.prototype.styleFloat;\\n\\n/**\\n * @type {string|number}\\n * @see http://msdn.microsoft.com/en-us/library/ms535169(VS.85).aspx\\n */\\nCSSProperties.prototype.zoom;\\n\\n/**\\n * @type {string}\\n * @see http://msdn.microsoft.com/en-us/library/ms535153(VS.85).aspx\\n */\\nCSSProperties.prototype.writingMode;\\n\\n/**\\n * IE-specific extensions.\\n * @see http://blogs.msdn.com/b/ie/archive/2008/09/08/microsoft-css-vendor-extensions.aspx\\n */\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsAccelerator;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsBackgroundPositionX;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsBackgroundPositionY;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsBehavior;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsBlockProgression;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsFilter;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsImeMode;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsLayoutGrid;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsLayoutGridChar;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsLayoutGridLine;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsLayoutGridMode;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsLayoutGridType;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsLineBreak;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsLineGridMode;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsInterpolationMode;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsOverflowX;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsOverflowY;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsScrollbar3dlightColor;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsScrollbarArrowColor;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsScrollbarBaseColor;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsScrollbarDarkshadowColor;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsScrollbarFaceColor;\\n\\nCSSProperties.prototype.MsScrollbarHighlightColor;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsScrollbarShadowColor;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsScrollbarTrackColor;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsTextAlignLast;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsTextAutospace;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsTextJustify;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsTextKashidaSpace;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsTextOverflow;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsTextUnderlinePosition;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsWordBreak;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsWordWrap;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsWritingMode;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsZoom;\\n\\n/** @type {string} */\\nCSSProperties.prototype.MsUserSelect;\\n\\n// See: http://msdn.microsoft.com/en-us/library/windows/apps/Hh702466.aspx\\n\\n/** @type {string} */\\nCSSProperties.prototype.msContentZooming;\\n\\n/** @type {string} */\\nCSSProperties.prototype.msTouchAction;\\n\\n/** @type {string} */\\nCSSProperties.prototype.msTransform;\\n\\n/** @type {string} */\\nCSSProperties.prototype.msTransition;\\n","externs/webkit_css.js":"/*\\n * Copyright 2008 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for WebKit\'s custom CSS properties. Copied from:\\n * {@link\\n * http://trac.webkit.org/browser/trunk/Source/WebCore/css/CSSPropertyNames.in}\\n *\\n * If you make changes to this file, notice that every property appears\\n * twice: once as an uppercase name and once as a lowercase name.\\n * WebKit allows both. The uppercase version is preferred.\\n *\\n * @externs\\n * @author nicksantos@google.com (Nick Santos)\\n * @author mastepien@google.com (Marek Stepien)\\n */\\n\\n/** @type {string} */ CSSProperties.prototype.WebkitAlignContent;\\n/** @type {string} */ CSSProperties.prototype.WebkitAlignItems;\\n/** @type {string} */ CSSProperties.prototype.WebkitAlignSelf;\\n/** @type {string} */ CSSProperties.prototype.WebkitAnimation;\\n/** @type {string} */ CSSProperties.prototype.WebkitAnimationDelay;\\n/** @type {string} */ CSSProperties.prototype.WebkitAnimationDirection;\\n/** @type {string} */ CSSProperties.prototype.WebkitAnimationDuration;\\n/** @type {string} */ CSSProperties.prototype.WebkitAnimationFillMode;\\n/** @type {string} */ CSSProperties.prototype.WebkitAnimationIterationCount;\\n/** @type {string} */ CSSProperties.prototype.WebkitAnimationName;\\n/** @type {string} */ CSSProperties.prototype.WebkitAnimationPlayState;\\n/** @type {string} */ CSSProperties.prototype.WebkitAnimationTimingFunction;\\n/** @type {string} */ CSSProperties.prototype.WebkitAppearance;\\n/** @type {string} */ CSSProperties.prototype.WebkitAppRegion;\\n/** @type {string} */ CSSProperties.prototype.WebkitAspectRatio;\\n/** @type {string} */ CSSProperties.prototype.WebkitBackfaceVisibility;\\n/** @type {string} */ CSSProperties.prototype.WebkitBackgroundClip;\\n/** @type {string} */ CSSProperties.prototype.WebkitBackgroundComposite;\\n/** @type {string} */ CSSProperties.prototype.WebkitBackgroundOrigin;\\n/** @type {string} */ CSSProperties.prototype.WebkitBackgroundSize;\\n/** @type {string} */ CSSProperties.prototype.WebkitBinding;\\n/** @type {string} */ CSSProperties.prototype.WebkitBlendMode;\\n/** @type {string} */ CSSProperties.prototype.WebkitBorderAfter;\\n/** @type {string} */ CSSProperties.prototype.WebkitBorderAfterColor;\\n/** @type {string} */ CSSProperties.prototype.WebkitBorderAfterStyle;\\n/** @type {string} */ CSSProperties.prototype.WebkitBorderAfterWidth;\\n/** @type {string} */ CSSProperties.prototype.WebkitBorderBefore;\\n/** @type {string} */ CSSProperties.prototype.WebkitBorderBeforeColor;\\n/** @type {string} */ CSSProperties.prototype.WebkitBorderBeforeStyle;\\n/** @type {string} */ CSSProperties.prototype.WebkitBorderBeforeWidth;\\n/** @type {string} */ CSSProperties.prototype.WebkitBorderBottomLeftRadius;\\n/** @type {string} */ CSSProperties.prototype.WebkitBorderBottomRightRadius;\\n/** @type {string} */ CSSProperties.prototype.WebkitBorderEnd;\\n/** @type {string} */ CSSProperties.prototype.WebkitBorderEndColor;\\n/** @type {string} */ CSSProperties.prototype.WebkitBorderEndStyle;\\n/** @type {string} */ CSSProperties.prototype.WebkitBorderEndWidth;\\n/** @type {string} */ CSSProperties.prototype.WebkitBorderFit;\\n/** @type {string} */ CSSProperties.prototype.WebkitBorderHorizontalSpacing;\\n/** @type {string} */ CSSProperties.prototype.WebkitBorderImage;\\n/** @type {string} */ CSSProperties.prototype.WebkitBorderRadius;\\n/** @type {string} */ CSSProperties.prototype.WebkitBorderStart;\\n/** @type {string} */ CSSProperties.prototype.WebkitBorderStartColor;\\n/** @type {string} */ CSSProperties.prototype.WebkitBorderStartStyle;\\n/** @type {string} */ CSSProperties.prototype.WebkitBorderStartWidth;\\n/** @type {string} */ CSSProperties.prototype.WebkitBorderTopLeftRadius;\\n/** @type {string} */ CSSProperties.prototype.WebkitBorderTopRightRadius;\\n/** @type {string} */ CSSProperties.prototype.WebkitBorderVerticalSpacing;\\n/** @type {string} */ CSSProperties.prototype.WebkitBoxAlign;\\n/** @type {string} */ CSSProperties.prototype.WebkitBoxDecorationBreak;\\n/** @type {string} */ CSSProperties.prototype.WebkitBoxDirection;\\n/** @type {string} */ CSSProperties.prototype.WebkitBoxFlex;\\n/** @type {string} */ CSSProperties.prototype.WebkitBoxFlexGroup;\\n/** @type {string} */ CSSProperties.prototype.WebkitBoxLines;\\n/** @type {string} */ CSSProperties.prototype.WebkitBoxOrdinalGroup;\\n/** @type {string} */ CSSProperties.prototype.WebkitBoxOrient;\\n/** @type {string} */ CSSProperties.prototype.WebkitBoxPack;\\n/** @type {string} */ CSSProperties.prototype.WebkitBoxReflect;\\n/** @type {string} */ CSSProperties.prototype.WebkitBoxShadow;\\n/** @type {string} */ CSSProperties.prototype.WebkitBoxSizing;\\n/** @type {string} */ CSSProperties.prototype.WebkitColorCorrection;\\n/** @type {string} */ CSSProperties.prototype.WebkitColumnAxis;\\n/** @type {string} */ CSSProperties.prototype.WebkitColumnBreakAfter;\\n/** @type {string} */ CSSProperties.prototype.WebkitColumnBreakBefore;\\n/** @type {string} */ CSSProperties.prototype.WebkitColumnBreakInside;\\n/** @'; +a.a+="type {string} */ CSSProperties.prototype.WebkitColumnCount;\\n/** @type {string} */ CSSProperties.prototype.WebkitColumnGap;\\n/** @type {string} */ CSSProperties.prototype.WebkitColumnProgression;\\n/** @type {string} */ CSSProperties.prototype.WebkitColumnRule;\\n/** @type {string} */ CSSProperties.prototype.WebkitColumnRuleColor;\\n/** @type {string} */ CSSProperties.prototype.WebkitColumnRuleStyle;\\n/** @type {string} */ CSSProperties.prototype.WebkitColumnRuleWidth;\\n/** @type {string} */ CSSProperties.prototype.WebkitColumns;\\n/** @type {string} */ CSSProperties.prototype.WebkitColumnSpan;\\n/** @type {string} */ CSSProperties.prototype.WebkitColumnWidth;\\n/** @type {string} */ CSSProperties.prototype.WebkitDashboardRegion;\\n/** @type {string} */ CSSProperties.prototype.WebkitFilter;\\n/** @type {string} */ CSSProperties.prototype.WebkitFlex;\\n/** @type {string} */ CSSProperties.prototype.WebkitFlexBasis;\\n/** @type {string} */ CSSProperties.prototype.WebkitFlexDirection;\\n/** @type {string} */ CSSProperties.prototype.WebkitFlexFlow;\\n/** @type {string} */ CSSProperties.prototype.WebkitFlexGrow;\\n/** @type {string} */ CSSProperties.prototype.WebkitFlexShrink;\\n/** @type {string} */ CSSProperties.prototype.WebkitFlexWrap;\\n/** @type {string} */ CSSProperties.prototype.WebkitFlowFrom;\\n/** @type {string} */ CSSProperties.prototype.WebkitFlowInto;\\n/** @type {string} */ CSSProperties.prototype.WebkitFontSizeDelta;\\n/** @type {string} */ CSSProperties.prototype.WebkitFontSmoothing;\\n/** @type {string} */ CSSProperties.prototype.WebkitGridColumn;\\n/** @type {string} */ CSSProperties.prototype.WebkitGridColumns;\\n/** @type {string} */ CSSProperties.prototype.WebkitGridRow;\\n/** @type {string} */ CSSProperties.prototype.WebkitGridRows;\\n/** @type {string} */ CSSProperties.prototype.WebkitHighlight;\\n/** @type {string} */ CSSProperties.prototype.WebkitHyphenateCharacter;\\n/** @type {string} */ CSSProperties.prototype.WebkitHyphenateLimitAfter;\\n/** @type {string} */ CSSProperties.prototype.WebkitHyphenateLimitBefore;\\n/** @type {string} */ CSSProperties.prototype.WebkitHyphenateLimitLines;\\n/** @type {string} */ CSSProperties.prototype.WebkitHyphens;\\n/** @type {string} */ CSSProperties.prototype.WebkitJustifyContent;\\n/** @type {string} */ CSSProperties.prototype.WebkitLineAlign;\\n/** @type {string} */ CSSProperties.prototype.WebkitLineBoxContain;\\n/** @type {string} */ CSSProperties.prototype.WebkitLineBreak;\\n/** @type {string} */ CSSProperties.prototype.WebkitLineClamp;\\n/** @type {string} */ CSSProperties.prototype.WebkitLineGrid;\\n/** @type {string} */ CSSProperties.prototype.WebkitLineSnap;\\n/** @type {string} */ CSSProperties.prototype.WebkitLocale;\\n/** @type {string} */ CSSProperties.prototype.WebkitLogicalHeight;\\n/** @type {string} */ CSSProperties.prototype.WebkitLogicalWidth;\\n/** @type {string} */ CSSProperties.prototype.WebkitMarginAfter;\\n/** @type {string} */ CSSProperties.prototype.WebkitMarginAfterCollapse;\\n/** @type {string} */ CSSProperties.prototype.WebkitMarginBefore;\\n/** @type {string} */ CSSProperties.prototype.WebkitMarginBeforeCollapse;\\n/** @type {string} */ CSSProperties.prototype.WebkitMarginBottomCollapse;\\n/** @type {string} */ CSSProperties.prototype.WebkitMarginCollapse;\\n/** @type {string} */ CSSProperties.prototype.WebkitMarginEnd;\\n/** @type {string} */ CSSProperties.prototype.WebkitMarginStart;\\n/** @type {string} */ CSSProperties.prototype.WebkitMarginTopCollapse;\\n/** @type {string} */ CSSProperties.prototype.WebkitMarquee;\\n/** @type {string} */ CSSProperties.prototype.WebkitMarqueeDirection;\\n/** @type {string} */ CSSProperties.prototype.WebkitMarqueeIncrement;\\n/** @type {string} */ CSSProperties.prototype.WebkitMarqueeRepetition;\\n/** @type {string} */ CSSProperties.prototype.WebkitMarqueeSpeed;\\n/** @type {string} */ CSSProperties.prototype.WebkitMarqueeStyle;\\n/** @type {string} */ CSSProperties.prototype.WebkitMask;\\n/** @type {string} */ CSSProperties.prototype.WebkitMaskAttachment;\\n/** @type {string} */ CSSProperties.prototype.WebkitMaskBoxImage;\\n/** @type {string} */ CSSProperties.prototype.WebkitMaskBoxImageOutset;\\n/** @type {string} */ CSSProperties.prototype.WebkitMaskBoxImageRepeat;\\n/** @type {string} */ CSSProperties.prototype.WebkitMaskBoxImageSlice;\\n/** @type {string} */ CSSProperties.prototype.WebkitMaskBoxImageSource;\\n/** @type {string} */ CSSProperties.prototype.WebkitMaskBoxImageWidth;\\n/** @type {string} */ CSSProperties.prototype.WebkitMaskClip;\\n/** @type {string} */ CSSProperties.prototype.WebkitMaskComposite;\\n/** @type {string} */ CSSProperties.prototype.WebkitMaskImage;\\n/** @type {string} */ CSSProperties.prototype.WebkitMaskOrigin;\\n/** @type {string} */ CSSProperties.prototype.WebkitMaskPosition;\\n/** @type {string} */ CSSProperties.prototype.WebkitMaskPositionX;\\n/** @type {string} */ CSSProperties.prototype.WebkitMaskPositionY;\\n/** @type {string} */ CSSProperties.prototype.WebkitMaskRepeat;\\n/** @type {string} */ CSSProperties.prototype.WebkitMaskRepeatX;\\n/** @type {string} */ CSSProperties.prototype.WebkitMaskRepeatY;\\n/** @type {string} */ CSSProperties.prototype.WebkitMaskSize;\\n/** @type {string} */\\nCSSProperties.prototype.WebkitMatchNearestMailBlockquoteColor;\\n/** @type {string} */ CSSProperties.prototype.WebkitMaxLogicalHeight;\\n/** @type {string} */ CSSProperties.prototype.WebkitMaxLogicalWidth;\\n/** @type {string} */ CSSProperties.prototype.WebkitMinLogicalHeight;\\n/** @type {string} */ CSSProperties.prototype.WebkitMinLogicalWidth;\\n/** @type {string} */ CSSProperties.prototype.WebkitNbspMode;\\n/** @type {string} */ CSSProperties.prototype.WebkitOrder;\\n/** @type {string} */ CSSProperties.prototype.WebkitOverflowScrolling;\\n/** @type {string} */ CSSProperties.prototype.WebkitPaddingAfter;\\n/** @type {string} */ CSSProperties.prototype.WebkitPaddingBefore;\\n/** @type {string} */ CSSProperties.prototype.WebkitPaddingEnd;\\n/** @type {string} */ CSSProperties.prototype.WebkitPaddingStart;\\n/** @type {string} */ CSSProperties.prototype.WebkitPerspective;\\n/** @type {string} */ CSSProperties.prototype.WebkitPerspectiveOrigin;\\n/** @type {string} */ CSSProperties.prototype.WebkitPerspectiveOriginX;\\n/** @type {string} */ CSSProperties.prototype.WebkitPerspectiveOriginY;\\n/** @type {string} */ CSSProperties.prototype.WebkitPrintColorAdjust;\\n/** @type {string} */ CSSProperties.prototype.WebkitRegionBreakAfter;\\n/** @type {string} */ CSSProperties.prototype.WebkitRegionBreakBefore;\\n/** @type {string} */ CSSProperties.prototype.WebkitRegionBreakInside;\\n/** @type {string} */ CSSProperties.prototype.WebkitRegionOverflow;\\n/** @type {string} */ CSSProperties.prototype.WebkitRtlOrdering;\\n/** @type {string} */ CSSProperties.prototype.WebkitRubyPosition;\\n/** @type {string} */ CSSProperties.prototype.WebkitShapeInside;\\n/** @type {string} */ CSSProperties.prototype.WebkitShapeMargin;\\n/** @type {string} */ CSSProperties.prototype.WebkitShapeOutside;\\n/** @type {string} */ CSSProperties.prototype.WebkitShapePadding;\\n/** @type {string} */ CSSProperties.prototype.WebkitTapHighlightColor;\\n/** @type {string} */ CSSProperties.prototype.WebkitTextAlignLast;\\n/** @type {string} */ CSSProperties.prototype.WebkitTextCombine;\\n/** @type {string} */ CSSProperties.prototype.WebkitTextDecorationLine;\\n/** @type {string} */ CSSProperties.prototype.WebkitTextDecorationsInEffect;\\n/** @type {string} */ CSSProperties.prototype.WebkitTextDecorationStyle;\\n/** @type {string} */ CSSProperties.prototype.WebkitTextEmphasis;\\n/** @type {string} */ CSSProperties.prototype.WebkitTextEmphasisColor;\\n/** @type {string} */ CSSProperties.prototype.WebkitTextEmphasisPosition;\\n/** @type {string} */ CSSProperties.prototype.WebkitTextEmphasisStyle;\\n/** @type {string} */ CSSProperties.prototype.WebkitTextFillColor;\\n/** @type {string} */ CSSProperties.prototype.WebkitTextOrientation;\\n/** @type {string} */ CSSProperties.prototype.WebkitTextSecurity;\\n/** @type {string} */ CSSProperties.prototype.WebkitTextSizeAdjust;\\n/** @type {string} */ CSSProperties.prototype.WebkitTextStroke;\\n/** @type {string} */ CSSProperties.prototype.WebkitTextStrokeColor;\\n/** @type {string} */ CSSProperties.prototype.WebkitTextStrokeWidth;\\n/** @type {string} */ CSSProperties.prototype.WebkitTransform;\\n/** @type {string} */ CSSProperties.prototype.WebkitTransformOrigin;\\n/** @type {string} */ CSSProperties.prototype.WebkitTransformOriginX;\\n/** @type {string} */ CSSProperties.prototype.WebkitTransformOriginY;\\n/** @type {string} */ CSSProperties.prototype.WebkitTransformOriginZ;\\n/** @type {string} */ CSSProperties.prototype.WebkitTransformStyle;\\n/** @type {string} */ CSSProperties.prototype.WebkitTransition;\\n/** @type {string} */ CSSProperties.prototype.WebkitTransitionDelay;\\n/** @type {string} */ CSSProperties.prototype.WebkitTransitionDuration;\\n/** @type {string} */ CSSProperties.prototype.WebkitTransitionProperty;\\n/** @type {string} */ CSSProperties.prototype.WebkitTransitionRepeatCount;\\n/** @type {string} */ CSSProperties.prototype.WebkitTransitionTimingFunction;\\n/** @type {string} */ CSSProperties.prototype.WebkitUserDrag;\\n/** @type {string} */ CSSProperties.prototype.WebkitUserModify;\\n/** @type {string} */ CSSProperties.prototype.WebkitUserSelect;\\n/** @type {string} */ CSSProperties.prototype.WebkitWrap;\\n/** @type {string} */ CSSProperties.prototype.WebkitWrapFlow;\\n/** @type {string} */ CSSProperties.prototype.WebkitWrapThrough;\\n/** @type {string} */ CSSProperties.prototype.WebkitWritingMode;\\n\\n// WebKit also adds bindings for the lowercase versions of these properties.\\n// The uppercase version is preferred.\\n\\n/** @type {string} */ CSSProperties.prototype.webkitAlignContent;\\n/** @type {string} */ CSSProperties.prototype.webkitAlignItems;\\n/** @type {string} */ CSSProperties.prototype.webkitAlignSelf;\\n/** @type {string} */ CSSProperties.prototype.webkitAnimation;\\n/** @type {string} */ CSSProperties.prototype.webkitAnimationDelay;\\n/** @type {string} */ CSSProperties.prototype.webkitAnimationDirection;\\n/** @type {string} */ CSSProperties.prototype.webkitAnimationDuration;\\n/** @type {string} */ CSSProperties.prototype.webkitAnimationFillMode;\\n/** @type {string} */ CSSProperties.prototype.webkitAnimationIterationCount;\\n/** @type {string} */ CSSProperties.prototype.webkitAnimationName;\\n/** @type {string} */ CSSProperties.prototype.webkitAnimationPlayState;\\n/** @type {string} */ CSSProperties.prototype.webkitAnimationTimingFunction;\\n/** @type {string} */ CSSProperties.prototype.webkitAppearance;\\n/** @type {string} */ CSSProperties.prototype.webkitAppRegion;\\n/** @type {string} */ CSSProperties.prototype.webkitAspectRatio;\\n/** @type {string} */ CSSProperties.prototype.webkitBackfaceVisibility;\\n/** @type {string} */ CSSProperties.prototype.webkitBackgroundClip;\\n/** @type {string} */ CSSProperties.prototype.webkitBackgroundComposite;\\n/** @type {string} */ CSSProperties.prototype.webkitBackgroundOrigin;\\n/** @type {string} */ CSSProperties.prototype.webkitBackgroundSize;\\n/** @type {string} */ CSSProperties.prototype.webkitBinding;\\n/** @type {string} */ CSSProperties.prototype.webkitBlendMode;\\n/** @type {string} */ CSSProperties.prototype.webkitBorderAfter;\\n/** @type {string} */ CSSProperties.prototype.webkitBorderAfterColor;\\n/** @type {string} */ CSSProperties.prototype.webkitBorderAfterStyle;\\n/** @type {string} */ CSSProperties.prototype.webkitBorderAfterWidth;\\n/** @type {string} */ CSSProperties.prototype.webkitBorderBefore;\\n/** @type {string} */ CSSProperties.prototype.webkitBorderBeforeColor;\\n/** @type {string} */ CSSProperties.prototype.webkitBorderBeforeStyle;\\n/** @type {string} */ CSSProperties.prototype.webkitBorderBeforeWidth;\\n/** @type {string} */ CSSProperties.prototype.webkitBorderBottomLeftRadius;\\n/** @type {string} */ CSSProperties.prototype.webkitBorderBottomRightRadius;\\n/** @type {string} */ CSSProperties.prototype.webkitBorderEnd;\\n/** @type {string} */ CSSProperties.prototype.webkitBorderEndColor;\\n/** @type {string} */ CSSProperties.prototype.webkitBorderEndStyle;\\n/** @type {string} */ CSSProperties.prototype.webkitBorderEndWidth;\\n/** @type {string} */ CSSProperties.prototype.webkitBorderFit;\\n/** @type {string} */ CSSProperties.prototype.webkitBorderHorizontalSpacing;\\n/** @type {string} */ CSSProperties.prototype.webkitBorderImage;\\n/** @type {string} */ CSSProperties.prototype.webkitBorderRadius;\\n/** @type {string} */ CSSProperties.prototype.webkitBorderStart;\\n/** @type {string} */ CSSProperties.prototype.webkitBorderStartColor;\\n/** @type {string} */ CSSProperties.prototype.webkitBorderStartStyle;\\n/** @type {string} */ CSSProperties.prototype.webkitBorderStartWidth;\\n/** @type {string} */ CSSProperties.prototype.webkitBorderTopLeftRadius;\\n/** @type {string} */ CSSProperties.prototype.webkitBorderTopRightRadius;\\n/** @type {string} */ CSSProperties.prototype.webkitBorderVerticalSpacing;\\n/** @type {string} */ CSSProperties.prototype.webkitBoxAlign;\\n/** @type {string} */ CSSProperties.prototype.webkitBoxDecorationBreak;\\n/** @type {string} */ CSSProperties.prototype.webkitBoxDirection;\\n/** @type {string} */ CSSProperties.prototype.webkitBoxFlex;\\n/** @type {string} */ CSSProperties.prototype.webkitBoxFlexGroup;\\n/** @type {string} */ CSSProperties.prototype.webkitBoxLines;\\n/** @type {string} */ CSSProperties.prototype.webkitBoxOrdinalGroup;\\n/** @type {string} */ CSSProperties.prototype.webkitBoxOrient;\\n/** @type {string} */ CSSProperties.prototype.webkitBoxPack;\\n/** @type {string} */ CSSProperties.prototype.webkitBoxReflect;\\n/** @type {string} */ CSSProperties.prototype.webkitBoxShadow;\\n/** @type {string} */ CSSProperties.prototype.webkitBoxSizing;\\n/** @type {string} */ CSSProperties.prototype.webkitColorCorrection;\\n/** @type {string} */ CSSProperties.prototype.webkitColumnAxis;\\n/** @type {string} */ CSSProperties.prototype.webkitColumnBreakAfter;\\n/** @type {string} */ CSSProperties.prototype.webkitColumnBreakBefore;\\n/** @type {string} */ CSSProperties.prototype.webkitColumnBreakInside;\\n/** @type {string} */ CSSProperties.prototype.webkitColumnCount;\\n/** @type {string} */ CSSProperties.prototype.webkitColumnGap;\\n/** @type {string} */ CSSProperties.prototype.webkitColumnProgression;\\n/** @type {string} */ CSSProperties.prototype.webkitColumnRule;\\n/** @type {string} */ CSSProperties.prototype.webkitColumnRuleColor;\\n/** @type {string} */ CSSProperties.prototype.webkitColumnRuleStyle;\\n/** @type {string} */ CSSProperties.prototype.webkitColumnRuleWidth;\\n/** @type {string} */ CSSProperties.prototype.webkitColumns;\\n/** @type {string} */ CSSProperties.prototype.webkitColumnSpan;\\n/** @type {string} */ CSSProperties.prototype.webkitColumnWidth;\\n/** @type {string} */ CSSProperties.prototype.webkitDashboardRegion;\\n/** @type {string} */ CSSProperties.prototype.webkitFilter;\\n/** @type {string} */ CSSProperties.prototype.webkitFlex;\\n/** @type {string} */ CSSProperties.prototype.webkitFlexBasis;\\n/** @type {string} */ CSSProperties.prototype.webkitFlexDirection;\\n/** @type {string} */ CSSProperties.prototype.webkitFlexFlow;\\n/** @type {string} */ CSSProperties.prototype.webkitFlexGrow;\\n/** @type {string} */ CSSProperties.prototype.webkitFlexShrink;\\n/** @type {string} */ CSSProperties.prototype.webkitFlexWrap;\\n/** @type {string} */ CSSProperties.prototype.webkitFlowFrom;\\n/** @type {string} */ CSSProperties.prototype.webkitFlowInto;\\n/** @type {string} */ CSSProperties.prototype.webkitFontSizeDelta;\\n/** @type {string} */ CSSProperties.prototype.webkitFontSmoothing;\\n/** @type {string} */ CSSProperties.prototype.webkitGridColumn;\\n/** @type {string} */ CSSProperties.prototype.webkitGridColumns;\\n/** @type {string} */ CSSProperties.prototype.webkitGridRow;\\n/** @type {string} */ CSSProperties.prototype.webkitGridRows;\\n/** @type {string} */ CSSProperties.prototype.webkitHighlight;\\n/** @type {string} */ CSSProperties.prototype.webkitHyphenateCharacter;\\n/** @type {string} */ CSSProperties.prototype.webkitHyphenateLimitAfter;\\n/** @type {string} */ CSSProperties.prototype.webkitHyphenateLimitBefore;\\n/** @type {string} */ CSSProperties.prototype.webkitHyphenateLimitLines;\\n/** @type {string} */ CSSProperties.prototype.webkitHyphens;\\n/** @type {string} */ CSSProperties.prototype.webkitJustifyContent;\\n/** @type {string} */ CSSProperties.prototype.webkitLineAlign;\\n/** @"; +a.a+='type {string} */ CSSProperties.prototype.webkitLineBoxContain;\\n/** @type {string} */ CSSProperties.prototype.webkitLineBreak;\\n/** @type {string} */ CSSProperties.prototype.webkitLineClamp;\\n/** @type {string} */ CSSProperties.prototype.webkitLineGrid;\\n/** @type {string} */ CSSProperties.prototype.webkitLineSnap;\\n/** @type {string} */ CSSProperties.prototype.webkitLocale;\\n/** @type {string} */ CSSProperties.prototype.webkitLogicalHeight;\\n/** @type {string} */ CSSProperties.prototype.webkitLogicalWidth;\\n/** @type {string} */ CSSProperties.prototype.webkitMarginAfter;\\n/** @type {string} */ CSSProperties.prototype.webkitMarginAfterCollapse;\\n/** @type {string} */ CSSProperties.prototype.webkitMarginBefore;\\n/** @type {string} */ CSSProperties.prototype.webkitMarginBeforeCollapse;\\n/** @type {string} */ CSSProperties.prototype.webkitMarginBottomCollapse;\\n/** @type {string} */ CSSProperties.prototype.webkitMarginCollapse;\\n/** @type {string} */ CSSProperties.prototype.webkitMarginEnd;\\n/** @type {string} */ CSSProperties.prototype.webkitMarginStart;\\n/** @type {string} */ CSSProperties.prototype.webkitMarginTopCollapse;\\n/** @type {string} */ CSSProperties.prototype.webkitMarquee;\\n/** @type {string} */ CSSProperties.prototype.webkitMarqueeDirection;\\n/** @type {string} */ CSSProperties.prototype.webkitMarqueeIncrement;\\n/** @type {string} */ CSSProperties.prototype.webkitMarqueeRepetition;\\n/** @type {string} */ CSSProperties.prototype.webkitMarqueeSpeed;\\n/** @type {string} */ CSSProperties.prototype.webkitMarqueeStyle;\\n/** @type {string} */ CSSProperties.prototype.webkitMask;\\n/** @type {string} */ CSSProperties.prototype.webkitMaskAttachment;\\n/** @type {string} */ CSSProperties.prototype.webkitMaskBoxImage;\\n/** @type {string} */ CSSProperties.prototype.webkitMaskBoxImageOutset;\\n/** @type {string} */ CSSProperties.prototype.webkitMaskBoxImageRepeat;\\n/** @type {string} */ CSSProperties.prototype.webkitMaskBoxImageSlice;\\n/** @type {string} */ CSSProperties.prototype.webkitMaskBoxImageSource;\\n/** @type {string} */ CSSProperties.prototype.webkitMaskBoxImageWidth;\\n/** @type {string} */ CSSProperties.prototype.webkitMaskClip;\\n/** @type {string} */ CSSProperties.prototype.webkitMaskComposite;\\n/** @type {string} */ CSSProperties.prototype.webkitMaskImage;\\n/** @type {string} */ CSSProperties.prototype.webkitMaskOrigin;\\n/** @type {string} */ CSSProperties.prototype.webkitMaskPosition;\\n/** @type {string} */ CSSProperties.prototype.webkitMaskPositionX;\\n/** @type {string} */ CSSProperties.prototype.webkitMaskPositionY;\\n/** @type {string} */ CSSProperties.prototype.webkitMaskRepeat;\\n/** @type {string} */ CSSProperties.prototype.webkitMaskRepeatX;\\n/** @type {string} */ CSSProperties.prototype.webkitMaskRepeatY;\\n/** @type {string} */ CSSProperties.prototype.webkitMaskSize;\\n/** @type {string} */\\nCSSProperties.prototype.webkitMatchNearestMailBlockquoteColor;\\n/** @type {string} */ CSSProperties.prototype.webkitMaxLogicalHeight;\\n/** @type {string} */ CSSProperties.prototype.webkitMaxLogicalWidth;\\n/** @type {string} */ CSSProperties.prototype.webkitMinLogicalHeight;\\n/** @type {string} */ CSSProperties.prototype.webkitMinLogicalWidth;\\n/** @type {string} */ CSSProperties.prototype.webkitNbspMode;\\n/** @type {string} */ CSSProperties.prototype.webkitOrder;\\n/** @type {string} */ CSSProperties.prototype.webkitOverflowScrolling;\\n/** @type {string} */ CSSProperties.prototype.webkitPaddingAfter;\\n/** @type {string} */ CSSProperties.prototype.webkitPaddingBefore;\\n/** @type {string} */ CSSProperties.prototype.webkitPaddingEnd;\\n/** @type {string} */ CSSProperties.prototype.webkitPaddingStart;\\n/** @type {string} */ CSSProperties.prototype.webkitPerspective;\\n/** @type {string} */ CSSProperties.prototype.webkitPerspectiveOrigin;\\n/** @type {string} */ CSSProperties.prototype.webkitPerspectiveOriginX;\\n/** @type {string} */ CSSProperties.prototype.webkitPerspectiveOriginY;\\n/** @type {string} */ CSSProperties.prototype.webkitPrintColorAdjust;\\n/** @type {string} */ CSSProperties.prototype.webkitRegionBreakAfter;\\n/** @type {string} */ CSSProperties.prototype.webkitRegionBreakBefore;\\n/** @type {string} */ CSSProperties.prototype.webkitRegionBreakInside;\\n/** @type {string} */ CSSProperties.prototype.webkitRegionOverflow;\\n/** @type {string} */ CSSProperties.prototype.webkitRtlOrdering;\\n/** @type {string} */ CSSProperties.prototype.webkitRubyPosition;\\n/** @type {string} */ CSSProperties.prototype.webkitShapeInside;\\n/** @type {string} */ CSSProperties.prototype.webkitShapeMargin;\\n/** @type {string} */ CSSProperties.prototype.webkitShapeOutside;\\n/** @type {string} */ CSSProperties.prototype.webkitShapePadding;\\n/** @type {string} */ CSSProperties.prototype.webkitTapHighlightColor;\\n/** @type {string} */ CSSProperties.prototype.webkitTextAlignLast;\\n/** @type {string} */ CSSProperties.prototype.webkitTextCombine;\\n/** @type {string} */ CSSProperties.prototype.webkitTextDecorationLine;\\n/** @type {string} */ CSSProperties.prototype.webkitTextDecorationsInEffect;\\n/** @type {string} */ CSSProperties.prototype.webkitTextDecorationStyle;\\n/** @type {string} */ CSSProperties.prototype.webkitTextEmphasis;\\n/** @type {string} */ CSSProperties.prototype.webkitTextEmphasisColor;\\n/** @type {string} */ CSSProperties.prototype.webkitTextEmphasisPosition;\\n/** @type {string} */ CSSProperties.prototype.webkitTextEmphasisStyle;\\n/** @type {string} */ CSSProperties.prototype.webkitTextFillColor;\\n/** @type {string} */ CSSProperties.prototype.webkitTextOrientation;\\n/** @type {string} */ CSSProperties.prototype.webkitTextSecurity;\\n/** @type {string} */ CSSProperties.prototype.webkitTextSizeAdjust;\\n/** @type {string} */ CSSProperties.prototype.webkitTextStroke;\\n/** @type {string} */ CSSProperties.prototype.webkitTextStrokeColor;\\n/** @type {string} */ CSSProperties.prototype.webkitTextStrokeWidth;\\n/** @type {string} */ CSSProperties.prototype.webkitTransform;\\n/** @type {string} */ CSSProperties.prototype.webkitTransformOrigin;\\n/** @type {string} */ CSSProperties.prototype.webkitTransformOriginX;\\n/** @type {string} */ CSSProperties.prototype.webkitTransformOriginY;\\n/** @type {string} */ CSSProperties.prototype.webkitTransformOriginZ;\\n/** @type {string} */ CSSProperties.prototype.webkitTransformStyle;\\n/** @type {string} */ CSSProperties.prototype.webkitTransition;\\n/** @type {string} */ CSSProperties.prototype.webkitTransitionDelay;\\n/** @type {string} */ CSSProperties.prototype.webkitTransitionDuration;\\n/** @type {string} */ CSSProperties.prototype.webkitTransitionProperty;\\n/** @type {string} */ CSSProperties.prototype.webkitTransitionRepeatCount;\\n/** @type {string} */ CSSProperties.prototype.webkitTransitionTimingFunction;\\n/** @type {string} */ CSSProperties.prototype.webkitUserDrag;\\n/** @type {string} */ CSSProperties.prototype.webkitUserModify;\\n/** @type {string} */ CSSProperties.prototype.webkitUserSelect;\\n/** @type {string} */ CSSProperties.prototype.webkitWrap;\\n/** @type {string} */ CSSProperties.prototype.webkitWrapFlow;\\n/** @type {string} */ CSSProperties.prototype.webkitWrapThrough;\\n/** @type {string} */ CSSProperties.prototype.webkitWritingMode;\\n\\n/**\\n * @constructor\\n * @param {number} x\\n * @param {number} y\\n */\\nfunction WebKitPoint(x, y) {}\\n\\n/** @type {number} */\\nWebKitPoint.prototype.x;\\n\\n/** @type {number} */\\nWebKitPoint.prototype.y;\\n","externs/google_legacy.js":"/*\\n * Copyright 2010 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Declaration of the type level google namespace.\\n * @externs\\n * @author nicksantos@google.com (Nick Santos)\\n */\\n\\n/**\\n * Suppresses the compiler warning when multiple externs files declare the\\n * google namespace.\\n * @suppress {duplicate,strictMissingProperties}\\n * NOTE: This definition should be marked \\\\@const, and when it is we can remove\\n * the \\"strictMissingProperties\\" suppression.\\n */\\n// TODO(nicksantos): Consolidate to one google namespace declaration.\\nvar google = {};\\n","externs/deprecated.js":"/*\\n * Copyright 2008 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview JavaScript Built-Ins that are not part of any specifications\\n * but are still needed in some project\'s build.\\n * @externs\\n */\\n\\nvar opera = {};\\n\\nopera.postError;\\n\\n/** @nosideeffects */\\nopera.version = function() {};\\n\\n/** @constructor */\\nfunction XSLTProcessor() {}\\n\\n/**\\n * @constructor\\n * @extends {HTMLOptionElement}\\n * @param {*=} opt_text\\n * @param {*=} opt_value\\n * @param {*=} opt_defaultSelected\\n * @param {*=} opt_selected\\n */\\nfunction Option(opt_text, opt_value, opt_defaultSelected, opt_selected) {}\\n\\n\\n// The \\"methods\\" object is a place to hang arbitrary external\\n// properties. It is a throwback to pre-typed days, and should\\n// not be used for any new definitions; it exists only to bridge\\n// the gap between the old way and the new way.\\nvar methods = {};\\n","externs/es6_proxy.js":"/*\\n * Copyright 2018 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for ECMAScript 6 Proxy objects.\\n * @see https://tc39.github.io/ecma262/#sec-proxy-objects\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy\\n * @externs\\n */\\n\\n\\n/**\\n * @record\\n * @template TARGET\\n * @see https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler\\n */\\nfunction ProxyHandler() {}\\n\\n/**\\n * @type {(function(TARGET):?Object)|undefined}\\n * @see https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-getprototypeof\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/getPrototypeOf\\n */\\nProxyHandler.prototype.getPrototypeOf /* = function(target) {} */;\\n\\n/**\\n * @type {(function(TARGET, ?Object):boolean)|undefined}\\n * @see https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-setprototypeof-v\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/setPrototypeOf\\n */\\nProxyHandler.prototype.setPrototypeOf /* = function(target, proto) {} */;\\n\\n/**\\n * @type {(function(TARGET):boolean)|undefined}\\n * @see https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-isextensible\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/isExtensible\\n */\\nProxyHandler.prototype.isExtensible /* = function(target) {} */;\\n\\n/**\\n * @type {(function(TARGET):boolean)|undefined}\\n * @see https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-preventextensions\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/preventExtensions\\n */\\nProxyHandler.prototype.preventExtensions /* = function(target) {} */;\\n\\n/**\\n * @type {(function(TARGET, (string|symbol)):(!ObjectPropertyDescriptor|undefined))|undefined}\\n * @see https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-getownproperty-p\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/getOwnPropertyDescriptor\\n */\\nProxyHandler.prototype.getOwnPropertyDescriptor /* = function(target, prop) {} */;\\n\\n/**\\n * @type {(function(TARGET, (string|symbol), !ObjectPropertyDescriptor):boolean)|undefined}\\n * @see https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-defineownproperty-p-desc\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/defineProperty\\n */\\nProxyHandler.prototype.defineProperty /* = function(target, prop, desc) {} */;\\n\\n/**\\n * @type {(function(TARGET, (string|symbol)):boolean)|undefined}\\n * @see https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/has\\n */\\nProxyHandler.prototype.has /* = function(target, prop) {} */;\\n\\n/**\\n * @type {(function(TARGET, (string|symbol), !Object):*)|undefined}\\n * @see https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/get\\n */\\nProxyHandler.prototype.get /* = function(target, prop, receiver) {} */;\\n\\n/**\\n * @type {(function(TARGET, (string|symbol), *, !Object):boolean)|undefined}\\n * @see https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/set\\n */\\nProxyHandler.prototype.set /* = function(target, prop, value, receiver) {} */;\\n\\n/**\\n * @type {(function(TARGET, (string|symbol)):boolean)|undefined}\\n * @see https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-delete-p\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/deleteProperty\\n */\\nProxyHandler.prototype.deleteProperty /* = function (target, prop) {} */;\\n\\n/**\\n * @type {(function(TARGET):!Array<(string|symbol)>)|undefined}\\n * @see https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-ownpropertykeys\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/ownKeys\\n */\\nProxyHandler.prototype.ownKeys /* = function(target) {} */;\\n\\n/**\\n * @type {(function(TARGET, *, !Array):*)|undefined}\\n * @see https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-call-thisargument-argumentslist\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/apply\\n */\\nProxyHandler.prototype.apply /* = function(target, thisArg, argList) {} */;\\n\\n/**\\n * @type {(function(TARGET, !Array, function(new: ?, ...?)):!Object)|undefined}\\n * @see https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-construct-argumentslist-newtarget\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/construct\\n */\\nProxyHandler.prototype.construct /* = function(target, argList, newTarget) {} */;\\n\\n\\n/**\\n * @constructor\\n * @param {TARGET} target\\n * @param {!ProxyHandler} handler\\n * @template TARGET\\n * @see https://tc39.github.io/ecma262/#sec-proxy-constructor\\n * @see https'; +a.a+='://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy#Syntax\\n */\\nfunction Proxy(target, handler) {}\\n\\n/**\\n * @param {TARGET} target\\n * @param {!ProxyHandler} handler\\n * @return {{proxy: !Proxy, revoke: function():void}}\\n * @template TARGET\\n * @see https://tc39.github.io/ecma262/#sec-proxy.revocable\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/revocable\\n */\\nProxy.revocable = function(target, handler) {};\\n","externs/fido.js":"/*\\n * Copyright 2018 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions from the FIDO Specifications\\n * @see https://fidoalliance.org/download/\\n *\\n * @externs\\n * @author arnarbi@gmail.com (Arnar Birgisson)\\n */\\n\\n/**\\n * U2F JavaScript API namespace\\n * @see https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-javascript-api-v1.2-ps-20170411.html\\n * @const\\n */\\nvar u2f = {};\\n\\n/**\\n * Data object for a single sign request.\\n * @typedef {string}\\n */\\nu2f.Transport;\\n\\n/**\\n * Data object for a registered key.\\n * @typedef {{\\n * version: string,\\n * keyHandle: string,\\n * transports: (!Array|undefined),\\n * appId: ?string\\n * }}\\n */\\nu2f.RegisteredKey;\\n\\n/**\\n * An error object for responses\\n * @typedef {{\\n * errorCode: number,\\n * errorMessage: ?string\\n * }}\\n */\\nu2f.Error;\\n\\n/**\\n * Data object for a sign response.\\n * @typedef {{\\n * keyHandle: string,\\n * signatureData: string,\\n * clientData: string\\n * }}\\n */\\nu2f.SignResponse;\\n\\n/**\\n * @typedef {{\\n * version: string,\\n * challenge: string\\n * }}\\n */\\nu2f.RegisterRequest\\n\\n/**\\n * @param {string} appId\\n * @param {string} challenge\\n * @param {!Array} registeredKeys\\n * @param {function((!u2f.Error|!u2f.SignResponse))} callback\\n * @param {number=} opt_timeoutSeconds\\n */\\nu2f.sign = function(\\n appId, challenge, registeredKeys, callback, opt_timeoutSeconds) {};\\n\\n/**\\n * @param {string} appId\\n * @param {!Array} registerRequests\\n * @param {!Array} registeredKeys\\n * @param {function((!u2f.Error|!u2f.SignResponse))} callback\\n * @param {number=} opt_timeoutSeconds\\n */\\nu2f.register = function(\\n appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) {};\\n","externs/fileapi.js":"/*\\n * Copyright 2010 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n/**\\n * @fileoverview Definitions for objects in the File API, File Writer API, and\\n * File System API. Details of the API are at:\\n * http://www.w3.org/TR/FileAPI/\\n * http://www.w3.org/TR/file-writer-api/\\n * http://www.w3.org/TR/file-system-api/\\n *\\n * @externs\\n * @author dbk@google.com (David Barrett-Kahn)\\n * @author mpd@google.com (Michael Davidson)\\n */\\n\\n/** @record */\\nfunction BlobPropertyBag() {};\\n\\n/** @type {(string|undefined)} */\\nBlobPropertyBag.prototype.type;\\n\\n/**\\n * @see http://dev.w3.org/2006/webapi/FileAPI/#dfn-Blob\\n * @param {Array=} opt_blobParts\\n * @param {BlobPropertyBag=} opt_options\\n * @constructor\\n * @nosideeffects\\n */\\nfunction Blob(opt_blobParts, opt_options) {}\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-size\\n * @type {number}\\n */\\nBlob.prototype.size;\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-type\\n * @type {string}\\n */\\nBlob.prototype.type;\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-slice\\n * @param {number=} start\\n * @param {number=} length\\n * @param {string=} opt_contentType\\n * @return {!Blob}\\n * @nosideeffects\\n */\\nBlob.prototype.slice = function(start, length, opt_contentType) {};\\n\\n/**\\n * This replaces Blob.slice in Chrome since WebKit revision 84005.\\n * @see http://lists.w3.org/Archives/Public/public-webapps/2011AprJun/0222.html\\n * @param {number=} start\\n * @param {number=} end\\n * @param {string=} opt_contentType\\n * @return {!Blob}\\n * @nosideeffects\\n */\\nBlob.prototype.webkitSlice = function(start, end, opt_contentType) {};\\n\\n/**\\n * This replaces Blob.slice in Firefox.\\n * @see http://lists.w3.org/Archives/Public/public-webapps/2011AprJun/0222.html\\n * @param {number=} start\\n * @param {number=} end\\n * @param {string=} opt_contentType\\n * @return {!Blob}\\n * @nosideeffects\\n */\\nBlob.prototype.mozSlice = function(start, end, opt_contentType) {};\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#the-blobbuilder-interface\\n * @constructor\\n */\\nfunction BlobBuilder() {}\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#widl-BlobBuilder-append0\\n * @see http://www.w3.org/TR/file-writer-api/#widl-BlobBuilder-append1\\n * @see http://www.w3.org/TR/file-writer-api/#widl-BlobBuilder-append2\\n * @param {string|Blob|ArrayBuffer} data\\n * @param {string=} endings\\n * @return {undefined}\\n */\\nBlobBuilder.prototype.append = function(data, endings) {};\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#widl-BlobBuilder-getBlob\\n * @param {string=} contentType\\n * @return {!Blob}\\n */\\nBlobBuilder.prototype.getBlob = function(contentType) {};\\n\\n/**\\n * This has replaced BlobBuilder in Chrome since WebKit revision 84008.\\n * @see http://lists.w3.org/Archives/Public/public-webapps/2011AprJun/0222.html\\n * @constructor\\n */\\nfunction WebKitBlobBuilder() {}\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#widl-BlobBuilder-append0\\n * @see http://www.w3.org/TR/file-writer-api/#widl-BlobBuilder-append1\\n * @see http://www.w3.org/TR/file-writer-api/#widl-BlobBuilder-append2\\n * @param {string|Blob|ArrayBuffer} data\\n * @param {string=} endings\\n * @return {undefined}\\n */\\nWebKitBlobBuilder.prototype.append = function(data, endings) {};\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#widl-BlobBuilder-getBlob\\n * @param {string=} contentType\\n * @return {!Blob}\\n */\\nWebKitBlobBuilder.prototype.getBlob = function(contentType) {};\\n\\n\\n/**\\n * @record\\n * @see https://dev.w3.org/2009/dap/file-system/file-dir-sys.html#the-flags-dictionary\\n */\\nfunction FileSystemFlags() {};\\n\\n/** @type {(undefined|boolean)} */\\nFileSystemFlags.prototype.create;\\n\\n/** @type {(undefined|boolean)} */\\nFileSystemFlags.prototype.exclusive;\\n\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#the-directoryentry-interface\\n * @constructor\\n * @extends {Entry}\\n */\\nfunction DirectoryEntry() {};\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-DirectoryEntry-createReader\\n * @return {!DirectoryReader}\\n */\\nDirectoryEntry.prototype.createReader = function() {};\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-DirectoryEntry-getFile\\n * @param {string} path\\n * @param {!FileSystemFlags=} options\\n * @param {function(!FileEntry)=} successCallback\\n * @param {function(!FileError)=} errorCallback\\n * @return {undefined}\\n */\\nDirectoryEntry.prototype.getFile = function(path, options, successCallback,\\n errorCallback) {};\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-DirectoryEntry-getDirectory\\n * @param {string} path\\n * @param {!FileSystemFlags=} options\\n * @param {function(!DirectoryEntry)=} successCallback\\n * @param {function(!FileError)=} errorCallback\\n * @return {undefined}\\n */\\nDirectoryEntry.prototype.getDirectory = function(path, options, successCallback,\\n errorCallback) {};\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-DirectoryEntry-removeRecursively\\n * @param {function()} successCallback\\n * @param {function(!FileError)=} errorCallback\\n * @return {undefined}\\n */\\nDirectoryEntry.prototype.removeRecursively = function(successCallback,\\n errorCallback) {};\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#the-directoryreader-interface\\n * @constructor\\n */\\nfunction DirectoryReader() {};\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-DirectoryReader-readEntries\\n * @param {function(!Array)} successCallback\\n * @param {function(!FileError)=} errorCallback\\n * @return {undefined}\\n */\\nDirectoryReader.prototype.readEntries = function(successCallback,\\n errorCallback) {};\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#the-entry-interface\\n * @constructor\\n */\\nfunction Entry() {};\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-Entry-isFile\\n * @type {boolean}\\n */\\nEntry.prototype.isFile;\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-Entry-isDirectory\\n * @type {boolean}\\n */\\nEntry.prototype.isDirectory;\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-Entry-name\\n * @type {string}\\n */\\nEntry.prototype.name;\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-Entry-fullPath\\n * @type {string}\\n */\\nEntry.prototype.fullPath;\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-Entry-filesystem\\n * @type {!FileSystem}\\n */\\nEntry.prototype.filesystem;\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-Entry-moveTo\\n * @param {!DirectoryEntry} parent\\n * @param {string=} newName\\n * @param {function(!Entry)=} successCallback\\n * @param {function(!FileError)=} errorCallback\\n * @return {undefined}\\n */\\nEntry.prototype.moveTo = function(parent, newName, successCallback,\\n errorCallback) {};\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-Entry-copyTo\\n * @param {!DirectoryEntry} parent\\n * @param {string=} newName\\n * @param {function(!Entry)=} successCallback\\n * @param {function(!FileError)=} errorCallback\\n * @return {undefined}\\n */\\nEntry.prototype.copyTo = function(parent, newName, successCallback,\\n errorCallback) {};\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-Entry-toURL\\n * @param {string=} mimeType\\n * @return {string}\\n */\\nEntry.prototype.toURL = function(mimeType) {};\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-Entry-remove\\n * @param {function()} successCallback\\n * @param {function(!FileError)=} errorCallback\\n * @return {undefined}\\n */\\nEntry.prototype.remove = function(successCallback, errorCallback) {};\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-Entry-getMetadata\\n * @param {function(!Metadata)} successCallback\\n * @param {function(!FileError)=} errorCallback\\n * @return {undefined}\\n */\\nEntry.prototype.getMetadata = function(successCallback, errorCallback) {};\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-Entry-getParent\\n * @param {function(!Entry)} successCallback\\n * @param {function(!FileError)=} errorCallback\\n * @return {undefined}\\n */\\nEntry.prototype.getParent = function(successCallback, errorCallback) {};\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-file\\n * @param {!Array=} opt_contents\\n * @param {string=} opt_name\\n * @param {{type: (string|undefined), lastModified: (number|undefined)}=}\\n * opt_properties\\n * @constructor\\n * @extends {Blob}\\n */\\nfunction File(opt_contents, opt_name, opt_properties) {}\\n\\n/**\\n * Chrome uses this instead of name.\\n * @deprecated Use name instead.\\n * @type {string}\\n */\\nFile.prototype.fileName;\\n\\n/**\\n * Chrome uses this instead of size.\\n * @deprecated Use size instead.\\n * @type {string}\\n */\\nFile.prototype.fileSize;\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-name\\n * @type {string}\\n */\\nFile.prototype.name;\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-lastModifiedDate\\n * @type {Date}\\n */\\nFile.prototype.lastModifiedDate;\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-lastModified\\n * @type {number}\\n */\\nFile.prototype.lastModified;\\n\\n/**\\n * @see https://wicg.github.io/entries-api/#dom-file-webkitrelativepath\\n * @type {string}\\n */\\nFile.prototype.webkitRelativePath;\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#the-fileentry-interface\\n * @constructor\\n * @extends {Entry}\\n */\\nfunction FileEntry() {};\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-FileEntry-createWriter\\n * @param {function(!FileWriter)} successCallback\\n * @param {function(!FileError)=} errorCallback\\n * @return {undefined}\\n */\\nFileEntry.prototype.createWriter = function(successCallback, errorCallback) {};\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-FileEntry-file\\n * @param {function(!File)} successCallback\\n * @param {function(!FileError)=} errorCallback\\n * @return {undefined}\\n */\\nFileEntry.prototype.file = function(successCallback, errorCallback) {};\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#FileErrorInterface\\n * @constructor\\n * @extends {DOMError}\\n */\\nfunction FileError() {}\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-NOT_FOUND_ERR\\n * @type {number}\\n */\\nFileError.prototype.NOT_FOUND_ERR = 1;\\n\\n/** @type {number} */\\nFileError.NOT_FOUND_ERR = 1;\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-SECURITY_ERR\\n * @type {number}\\n */\\nFileError.prototype.SECURITY_ERR = 2;\\n\\n/** @type {number} */\\nFileError.SECURITY_ERR = 2;\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-ABORT_ERR\\n * @type {number}\\n */\\nFileError.prototype.ABORT_ERR = 3;\\n\\n/** @type {number} */\\nFileError.ABORT_ERR = 3;\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-NOT_READABLE_ERR\\n * @type {number}\\n */\\nFileError.prototype.NOT_READABLE_ERR = 4;\\n\\n/** @type {number} */\\nFileError.NOT_READABLE_ERR = 4;\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-ENCODING_ERR\\n * @type {number}\\n */\\nFileError.prototype.ENCODING_ERR = 5;\\n\\n/** @type {number} */\\nFileError.ENCODING_ERR = 5;\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#widl-FileError-NO_MODIFICATION_ALLOWED_ERR\\n * @type {number}\\n */\\nFileError.prototype.NO_MODIFICATION_ALLOWED_ERR = 6;\\n\\n/** @type {number} */\\nFileError.NO_MODIFICATION_ALLOWED_ERR = 6;\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#widl-FileException-INVALID_STATE_ERR\\n * @type {number}\\n */\\nFileError.prototype.INVALID_STATE_ERR = 7;\\n\\n/** @type {number} */\\nFileError.INVALID_STATE_ERR = 7;\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#widl-FileException-SYNTAX_ERR\\n * @type {number}\\n */\\nFileError.prototype.SYNTAX_ERR = 8;\\n\\n/** @type {number} */\\nFileError.SYNTAX_ERR = 8;\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-FileError-INVALID_MODIFICATION_ERR\\n * @type {number}\\n */\\nFileError.prototype.INVALID_MODIFICATION_ERR = 9;\\n\\n/** @type {number} */\\nFileError.INVALID_MODIFICATION_ERR = 9;\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-FileError-QUOTA_EXCEEDED_ERR\\n * @type {number}\\n */\\nFileError.prototype.QUOTA_EXCEEDED_ERR = 10;\\n\\n/** @type {number} */\\nFileError.QUOTA_EXCEEDED_ERR = 10;\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-FileException-TYPE_MISMATCH_ERR\\n * @type {number}\\n */\\nFileError.prototype.TYPE_MISMATCH_ERR = 11;\\n\\n/** @type {number} */\\nFileError.TYPE_MISMATCH_ERR = 11;\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-FileException-PATH_EXISTS_ERR\\n * @type {number}\\n */\\nFileError.prototype.PATH_EXISTS_ERR = 12;\\n\\n/** @type {number} */\\nFileError.PATH_EXISTS_ERR = 12;\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-code-exception\\n * @type {number}\\n * @deprecated Use the \'name\' or \'message\' attributes of DOMError rather than\\n * \'code\'\\n */\\nFileError.prototype.code;\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-filereader\\n * @constructor\\n * @implements {EventTarget}\\n */\\nfunction FileReader() {}\\n\\n/** @override */\\nFileReader.prototype.addEvent'; +a.a+='Listener = function(type, listener, opt_options) {\\n};\\n\\n/** @override */\\nFileReader.prototype.removeEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nFileReader.prototype.dispatchEvent = function(evt) {};\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-readAsArrayBuffer\\n * @param {!Blob} blob\\n * @return {undefined}\\n */\\nFileReader.prototype.readAsArrayBuffer = function(blob) {};\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-readAsBinaryStringAsync\\n * @param {!Blob} blob\\n * @return {undefined}\\n */\\nFileReader.prototype.readAsBinaryString = function(blob) {};\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-readAsText\\n * @param {!Blob} blob\\n * @param {string=} encoding\\n * @return {undefined}\\n */\\nFileReader.prototype.readAsText = function(blob, encoding) {};\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-readAsDataURL\\n * @param {!Blob} blob\\n * @return {undefined}\\n */\\nFileReader.prototype.readAsDataURL = function(blob) {};\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-abort\\n * @return {undefined}\\n */\\nFileReader.prototype.abort = function() {};\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-empty\\n * @type {number}\\n */\\nFileReader.prototype.EMPTY = 0;\\n\\n/** @type {number} */\\nFileReader.EMPTY = 0;\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-loading\\n * @type {number}\\n */\\nFileReader.prototype.LOADING = 1;\\n\\n/** @type {number} */\\nFileReader.LOADING = 1;\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-done\\n * @type {number}\\n */\\nFileReader.prototype.DONE = 2;\\n\\n/** @type {number} */\\nFileReader.DONE = 2;\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-readystate\\n * @type {number}\\n */\\nFileReader.prototype.readyState;\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-result\\n * @type {string|Blob|ArrayBuffer}\\n */\\nFileReader.prototype.result;\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-error\\n * @type {FileError}\\n */\\nFileReader.prototype.error;\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-onloadstart\\n * @type {?function(!ProgressEvent)}\\n */\\nFileReader.prototype.onloadstart;\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-onprogress\\n * @type {?function(!ProgressEvent)}\\n */\\nFileReader.prototype.onprogress;\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-onload\\n * @type {?function(!ProgressEvent)}\\n */\\nFileReader.prototype.onload;\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-onabort\\n * @type {?function(!ProgressEvent)}\\n */\\nFileReader.prototype.onabort;\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-onerror\\n * @type {?function(!ProgressEvent)}\\n */\\nFileReader.prototype.onerror;\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-onloadend\\n * @type {?function(!ProgressEvent)}\\n */\\nFileReader.prototype.onloadend;\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#idl-def-FileSaver\\n * @constructor\\n */\\nfunction FileSaver() {};\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#widl-FileSaver-abort\\n * @return {undefined}\\n */\\nFileSaver.prototype.abort = function() {};\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#widl-FileSaver-INIT\\n * @type {number}\\n */\\nFileSaver.prototype.INIT = 0;\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#widl-FileSaver-WRITING\\n * @type {number}\\n */\\nFileSaver.prototype.WRITING = 1;\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#widl-FileSaver-DONE\\n * @type {number}\\n */\\nFileSaver.prototype.DONE = 2;\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#widl-FileSaver-readyState\\n * @type {number}\\n */\\nFileSaver.prototype.readyState;\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#widl-FileSaver-error\\n * @type {FileError}\\n */\\nFileSaver.prototype.error;\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#widl-FileSaver-onwritestart\\n * @type {?function(!ProgressEvent)}\\n */\\nFileSaver.prototype.onwritestart;\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#widl-FileSaver-onprogress\\n * @type {?function(!ProgressEvent)}\\n */\\nFileSaver.prototype.onprogress;\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#widl-FileSaver-onwrite\\n * @type {?function(!ProgressEvent)}\\n */\\nFileSaver.prototype.onwrite;\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#widl-FileSaver-onabort\\n * @type {?function(!ProgressEvent)}\\n */\\nFileSaver.prototype.onabort;\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#widl-FileSaver-onerror\\n * @type {?function(!ProgressEvent)}\\n */\\nFileSaver.prototype.onerror;\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#widl-FileSaver-onwriteend\\n * @type {?function(!ProgressEvent)}\\n */\\nFileSaver.prototype.onwriteend;\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#the-filesystem-interface\\n * @constructor\\n */\\nfunction FileSystem() {}\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-FileSystem-name\\n * @type {string}\\n */\\nFileSystem.prototype.name;\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-FileSystem-root\\n * @type {!DirectoryEntry}\\n */\\nFileSystem.prototype.root;\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#idl-def-FileWriter\\n * @constructor\\n * @extends {FileSaver}\\n */\\nfunction FileWriter() {}\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#widl-FileWriter-position\\n * @type {number}\\n */\\nFileWriter.prototype.position;\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#widl-FileWriter-length\\n * @type {number}\\n */\\nFileWriter.prototype.length;\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#widl-FileWriter-write\\n * @param {!Blob} blob\\n * @return {undefined}\\n */\\nFileWriter.prototype.write = function(blob) {};\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#widl-FileWriter-seek\\n * @param {number} offset\\n * @return {undefined}\\n */\\nFileWriter.prototype.seek = function(offset) {};\\n\\n/**\\n * @see http://www.w3.org/TR/file-writer-api/#widl-FileWriter-truncate\\n * @param {number} size\\n * @return {undefined}\\n */\\nFileWriter.prototype.truncate = function(size) {};\\n\\n/**\\n * LocalFileSystem interface, implemented by Window and WorkerGlobalScope.\\n * @see http://www.w3.org/TR/file-system-api/#idl-def-LocalFileSystem\\n * @constructor\\n */\\nfunction LocalFileSystem() {}\\n\\n/**\\n * Metadata interface.\\n * @see http://www.w3.org/TR/file-system-api/#idl-def-Metadata\\n * @constructor\\n */\\nfunction Metadata() {}\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-Metadata-modificationTime\\n * @type {!Date}\\n */\\nMetadata.prototype.modificationTime;\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-Metadata-size\\n * @type {number}\\n */\\nMetadata.prototype.size;\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-LocalFileSystem-TEMPORARY\\n * @type {number}\\n*/\\nWindow.prototype.TEMPORARY = 0;\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-LocalFileSystem-PERSISTENT\\n * @type {number}\\n*/\\nWindow.prototype.PERSISTENT = 1;\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-LocalFileSystem-requestFileSystem\\n * @param {number} type\\n * @param {number} size\\n * @param {function(!FileSystem)} successCallback\\n * @param {function(!FileError)=} errorCallback\\n * @return {undefined}\\n */\\nfunction requestFileSystem(type, size, successCallback, errorCallback) {}\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-LocalFileSystem-requestFileSystem\\n * @param {number} type\\n * @param {number} size\\n * @param {function(!FileSystem)} successCallback\\n * @param {function(!FileError)=} errorCallback\\n * @return {undefined}\\n */\\nWindow.prototype.requestFileSystem = function(type, size, successCallback,\\n errorCallback) {};\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-LocalFileSystem-resolveLocalFileSystemURI\\n * @param {string} uri\\n * @param {function(!Entry)} successCallback\\n * @param {function(!FileError)=} errorCallback\\n * @return {undefined}\\n */\\nfunction resolveLocalFileSystemURI(uri, successCallback, errorCallback) {}\\n\\n/**\\n * @see http://www.w3.org/TR/file-system-api/#widl-LocalFileSystem-resolveLocalFileSystemURI\\n * @param {string} uri\\n * @param {function(!Entry)} successCallback\\n * @param {function(!FileError)=} errorCallback\\n * @return {undefined}\\n */\\nWindow.prototype.resolveLocalFileSystemURI = function(uri, successCallback,\\n errorCallback) {}\\n\\n/**\\n * This has replaced requestFileSystem in Chrome since WebKit revision 84224.\\n * @see http://www.w3.org/TR/file-system-api/#widl-LocalFileSystem-requestFileSystem\\n * @param {number} type\\n * @param {number} size\\n * @param {function(!FileSystem)} successCallback\\n * @param {function(!FileError)=} errorCallback\\n * @return {undefined}\\n */\\nfunction webkitRequestFileSystem(type, size, successCallback, errorCallback) {}\\n\\n/**\\n * This has replaced requestFileSystem in Chrome since WebKit revision 84224.\\n * @see http://www.w3.org/TR/file-system-api/#widl-LocalFileSystem-requestFileSystem\\n * @param {number} type\\n * @param {number} size\\n * @param {function(!FileSystem)} successCallback\\n * @param {function(!FileError)=} errorCallback\\n * @return {undefined}\\n */\\nWindow.prototype.webkitRequestFileSystem = function(type, size, successCallback,\\n errorCallback) {};\\n\\n/**\\n * This has replaced resolveLocalFileSystemURI in Chrome since WebKit revision\\n * 84224.\\n * @see http://www.w3.org/TR/file-system-api/#widl-LocalFileSystem-resolveLocalFileSystemURI\\n * @param {string} uri\\n * @param {function(!Entry)} successCallback\\n * @param {function(!FileError)=} errorCallback\\n * @return {undefined}\\n */\\nfunction webkitResolveLocalFileSystemURI(uri, successCallback, errorCallback) {}\\n\\n/**\\n * This has replaced resolveLocalFileSystemURI in Chrome since WebKit revision\\n * 84224.\\n * @see http://www.w3.org/TR/file-system-api/#widl-LocalFileSystem-resolveLocalFileSystemURI\\n * @param {string} uri\\n * @param {function(!Entry)} successCallback\\n * @param {function(!FileError)=} errorCallback\\n * @return {undefined}\\n */\\nWindow.prototype.webkitResolveLocalFileSystemURI = function(uri, successCallback,\\n errorCallback) {}\\n\\n// WindowBlobURIMethods interface, implemented by Window and WorkerGlobalScope.\\n// There are three APIs for this: the old specced API, the new specced API, and\\n// the webkit-prefixed API.\\n// @see http://www.w3.org/TR/FileAPI/#creating-revoking\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-createObjectURL\\n * @param {!Object} obj\\n * @return {string}\\n */\\nfunction createObjectURL(obj) {};\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-createObjectURL\\n * @param {!Object} obj\\n * @return {string}\\n */\\nWindow.prototype.createObjectURL = function(obj) {};\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-revokeObjectURL\\n * @param {string} url\\n * @return {undefined}\\n */\\nfunction revokeObjectURL(url) {};\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-revokeObjectURL\\n * @param {string} url\\n * @return {undefined}\\n */\\nWindow.prototype.revokeObjectURL = function(url) {};\\n\\n/**\\n * This has been replaced by URL in Chrome since WebKit revision 75739.\\n * @constructor\\n * @param {string} urlString\\n * @param {string=} opt_base\\n */\\nfunction webkitURL(urlString, opt_base) {}\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-createObjectURL\\n * @param {!Object} obj\\n * @return {string}\\n */\\nwebkitURL.createObjectURL = function(obj) {};\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-revokeObjectURL\\n * @param {string} url\\n * @return {undefined}\\n */\\nwebkitURL.revokeObjectURL = function(url) {};\\n\\n/**\\n * @see https://developers.google.com/chrome/whitepapers/storage\\n * @constructor\\n */\\nfunction StorageInfo() {}\\n\\n/**\\n * @see https://developers.google.com/chrome/whitepapers/storage\\n * @type {number}\\n * */\\nStorageInfo.prototype.TEMPORARY = 0;\\n\\n/**\\n * @see https://developers.google.com/chrome/whitepapers/storage\\n * @type {number}\\n */\\nStorageInfo.prototype.PERSISTENT = 1;\\n\\n/**\\n * @see https://developers.google.com/chrome/whitepapers/storage#requestQuota\\n * @param {number} type\\n * @param {number} size\\n * @param {function(number)} successCallback\\n * @param {function(!DOMException)=} errorCallback\\n * @return {undefined}\\n */\\nStorageInfo.prototype.requestQuota = function(type, size, successCallback,\\n errorCallback) {};\\n\\n/**\\n * @see https://developers.google.com/chrome/whitepapers/storage#queryUsageAndQuota\\n * @param {number} type\\n * @param {function(number, number)} successCallback\\n * @param {function(!DOMException)=} errorCallback\\n * @return {undefined}\\n */\\nStorageInfo.prototype.queryUsageAndQuota = function(type, successCallback,\\n errorCallback) {};\\n\\n/**\\n * @see https://developers.google.com/chrome/whitepapers/storage\\n * @type {!StorageInfo}\\n */\\nWindow.prototype.webkitStorageInfo;\\n\\n/**\\n * @see https://dvcs.w3.org/hg/quota/raw-file/tip/Overview.html#storagequota-interface.\\n * @constructor\\n */\\nfunction StorageQuota() {}\\n\\n/**\\n * @param {number} size\\n * @param {function(number)=} opt_successCallback\\n * @param {function(!DOMException)=} opt_errorCallback\\n * @return {undefined}\\n */\\nStorageQuota.prototype.requestQuota = function(size, opt_successCallback,\\n opt_errorCallback) {};\\n\\n/**\\n * @param {function(number, number)} successCallback\\n * @param {function(!DOMException)=} opt_errorCallback\\n * @return {undefined}\\n */\\nStorageQuota.prototype.queryUsageAndQuota = function(successCallback,\\n opt_errorCallback) {};\\n\\n\\n/**\\n * @type {!StorageQuota}\\n * @see https://developer.chrome.com/apps/offline_storage\\n */\\nNavigator.prototype.webkitPersistentStorage;\\n\\n/**\\n * @type {!StorageQuota}\\n * @see https://developer.chrome.com/apps/offline_storage\\n */\\nNavigator.prototype.webkitTemporaryStorage;\\n","externs/flash.js":"/*\\n * Copyright 2008 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n/**\\n * @fileoverview Definitions for all the Flash Object JavaScript methods. This\\n * file depends on w3c_dom2.js.\\n * Created from\\n * http://www.adobe.com/support/flash/publishexport/scriptingwithflash/scriptingwithflash_03.html\\n *\\n * @externs\\n * @author ajp@google.com (Andy Perelson)\\n */\\n\\n\\n// Standard Methods.\\n\\n/**\\n * Call a Flash function exported by ExternalInterface.\\n * @param {string} xmlString The XML string passed to Flash. The outer element\\n * should be {@code }. A sample invocation string:\\n * {@code \\n * test}\\n * @return {string} The serialized return value from Flash that you can eval.\\n */\\nHTMLObjectElement.prototype.CallFunction = function(xmlString) {};\\n\\n/**\\n * Returns the value of the Flash variable specified by varName or null if the\\n * variable does not exist.\\n * @param {string} varName The variable name.\\n * @return {string?} The variable value.\\n */\\nHTMLObjectElement.prototype.GetVariable = function(varName) {};\\n\\n/**\\n * Activates the frame number specified by `frameNumber` in the current\\n * movie.\\n * @param {number} frameNumber A non-negative integer frame number.\\n * @return {undefined}\\n */\\nHTMLObjectElement.prototype.GotoFrame = function(frameNumber) {};\\n\\n/**\\n * @return {boolean} Whether the movie is currently playing.\\n */\\nHTMLObjectElement.prototype.IsPlaying = function() {};\\n\\n/**\\n * Loads the movie identified by `url` to the layer specified by\\n * `layerNumber`.\\n * @param {number} layerNumber The layer number.\\n * @param {string} url The movie URL.\\n * @return {undefined}\\n */\\nHTMLObjectElement.prototype.LoadMovie = function(layerNumber, url) {};\\n\\n/**\\n * Pans a zoomed-in movie to the coordinates specified by x and y. Use mode to\\n * specify whether the values for x and y are pixels or a percent of the window.\\n * When mode is 0, the coordinates are pixels; when mode is 1, the coordinates\\n * are percent of the window.\\n * @param {number} x The x-coordinate.\\n * @par'; +a.a+='am {number} y The y-coordinate.\\n * @param {number} mode The mode.\\n * @return {undefined}\\n */\\nHTMLObjectElement.prototype.Pan = function(x, y, mode) {};\\n\\n/**\\n * @return {number} The percent of the Flash Player movie that has streamed\\n * into the browser so far; Possible values are from 0 to 100.\\n */\\nHTMLObjectElement.prototype.PercentLoaded = function() {};\\n\\n/**\\n * Starts playing the movie.\\n * @return {undefined}\\n */\\nHTMLObjectElement.prototype.Play = function() {};\\n\\n/**\\n * Goes to the first frame.\\n * @return {undefined}\\n */\\nHTMLObjectElement.prototype.Rewind = function() {};\\n\\n/**\\n * Sets the value of the flash variable.\\n * @param {string} variableName The variable name.\\n * @param {string} value The value.\\n * @return {undefined}\\n */\\nHTMLObjectElement.prototype.SetVariable = function(variableName, value) {};\\n\\n/**\\n * Zooms in on a rectangular area of the movie. The units of the coordinates\\n * are in twips (1440 units per inch).\\n * @param {number} left The left coordinate.\\n * @param {number} top The top coordinate.\\n * @param {number} right The right coordinate.\\n * @param {number} bottom The bottom coordinate.\\n * @return {undefined}\\n */\\nHTMLObjectElement.prototype.SetZoomRect = function(left, top, right, bottom) {};\\n\\n/**\\n * Stops playing the movie.\\n * @return {undefined}\\n */\\nHTMLObjectElement.prototype.StopPlay = function() {};\\n\\n/**\\n * @return {number} The total number of frames in the movie.\\n */\\nHTMLObjectElement.prototype.TotalFrames = function() {};\\n\\n/**\\n * Zooms the view by a relative scale factor.\\n * @param {number} percent The percentage scale factor, should be an integer.\\n * @return {undefined}\\n */\\nHTMLObjectElement.prototype.Zoom = function(percent) {};\\n\\n\\n// TellTarget Methods.\\n\\n/**\\n * Executes the action in the timeline specified by `target` in the\\n * specified frame.\\n * @param {string} target The timeline.\\n * @param {number} frameNumber The frame number.\\n * @return {undefined}\\n */\\nHTMLObjectElement.prototype.TCallFrame = function(target, frameNumber) {};\\n\\n/**\\n * Executes the action in the timeline specified by `target` in the\\n * specified frame.\\n * @param {string} target The timeline.\\n * @param {string} label The frame label.\\n * @return {undefined}\\n */\\nHTMLObjectElement.prototype.TCallLabel = function(target, label) {};\\n\\n/**\\n * Returns the number of the current frame for the specified timeline.\\n * @param {string} target The timeline.\\n * @return {number} The number of the current frame.\\n */\\nHTMLObjectElement.prototype.TCurentFrame = function(target) {};\\n\\n/**\\n * Returns the label of the current frame for the specified timeline.\\n * @param {string} target The timeline.\\n * @return {string} The label of the current frame, empty string if no\\n * current frame.\\n */\\nHTMLObjectElement.prototype.TCurrentLabel = function(target) {};\\n\\n/**\\n * Returns a string indicating the value of the property in the\\n * specified timeline.\\n * @param {string} target The timeline.\\n * @param {number} property The integer corresponding to the desired property.\\n * @return {string} The value of the property.\\n */\\nHTMLObjectElement.prototype.TGetProperty = function(target, property) {};\\n\\n/**\\n * Returns a number indicating the value of the property in the specified\\n * timeline.\\n * @param {string} target The timeline.\\n * @param {number} property The integer corresponding to the desired property.\\n * @return {number} A number indicating the value of the property.\\n */\\nHTMLObjectElement.prototype.TGetPropertyAsNumber = function(target, property) {};\\n\\n/**\\n * Goes to the specified frame number in the specified timeline.\\n * @param {string} target The timeline.\\n * @param {number} frameNumber The frame number.\\n * @return {undefined}\\n */\\nHTMLObjectElement.prototype.TGotoFrame = function(target, frameNumber) {};\\n\\n/**\\n * Goes to the specified frame label in the specified timeline.\\n * @param {string} target The timeline.\\n * @param {string} label The framelabel.\\n * @return {undefined}\\n */\\nHTMLObjectElement.prototype.TGotoLabel = function(target, label) {};\\n\\n/**\\n * Plays the specified timeline.\\n * @param {number} target The timeline.\\n * @return {undefined}\\n */\\nHTMLObjectElement.prototype.TPlay = function(target) {};\\n\\n/**\\n * Sets the value of the property in the specified timeline.\\n * @param {number} target The timeline.\\n * @param {number} property The integer corresponding to the desired property.\\n * @param {string|number} value The value.\\n * @return {undefined}\\n */\\nHTMLObjectElement.prototype.TSetProperty = function(target, property, value) {};\\n\\n/**\\n * Stops the specified timeline.\\n * @param {number} target The timeline.\\n * @return {undefined}\\n */\\nHTMLObjectElement.prototype.TStopPlay = function(target) {};\\n","externs/gecko_xml.js":"/*\\n * Copyright 2008 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for all the extensions over some of the\\n * W3C\'s XML specifications by Gecko. This file depends on\\n * w3c_xml.js. The whole file has been fully type annotated.\\n *\\n * @externs\\n * @author acleung@google.com (Alan Leung)\\n */\\n\\n/**\\n * XMLSerializer can be used to convert DOM subtree or DOM document into text.\\n * XMLSerializer is available to unprivileged scripts.\\n *\\n * XMLSerializer is mainly useful for applications and extensions based on\\n * Mozilla platform. While it\'s available to web pages, it\'s not part of any\\n * standard and level of support in other browsers is unknown.\\n *\\n * @constructor\\n */\\nfunction XMLSerializer() {}\\n\\n/**\\n * Returns the serialized subtree in the form of a string\\n * @param {Node} subtree\\n * @return {string}\\n */\\nXMLSerializer.prototype.serializeToString = function(subtree) {};\\n\\n/**\\n * The subtree rooted by the specified element is serialized to a byte stream\\n * using the character set specified.\\n *\\n * @param {Node} subtree\\n * @return {Object}\\n */\\nXMLSerializer.prototype.serializeToStream = function(subtree) {};\\n\\n/**\\n * DOMParser is mainly useful for applications and extensions based on Mozilla\\n * platform. While it\'s available to web pages, it\'s not part of any standard and\\n * level of support in other browsers is unknown.\\n *\\n * @constructor\\n */\\nfunction DOMParser() {}\\n\\n/**\\n * The string passed in is parsed into a DOM document.\\n *\\n * Example:\\n * var parser = new DOMParser();\\n * var doc = parser.parseFromString(aStr, \\"text/xml\\");\\n *\\n * @param {!TrustedHTML|string} src The UTF16 string to be parsed.\\n * @param {string} type The content type of the string.\\n * @return {Document}\\n */\\nDOMParser.prototype.parseFromString = function(src, type) {};\\n","externs/html5.js":"/*\\n * Copyright 2008 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n/**\\n * @fileoverview Definitions for all the extensions over the\\n * W3C\'s DOM3 specification in HTML5. This file depends on\\n * w3c_dom3.js. The whole file has been fully type annotated.\\n *\\n * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/index.html\\n * @see http://dev.w3.org/html5/spec/Overview.html\\n *\\n * This also includes Typed Array definitions from\\n * http://www.khronos.org/registry/typedarray/specs/latest/\\n *\\n * This relies on w3c_event.js being included first.\\n *\\n * @externs\\n * @author acleung@google.com (Alan Leung)\\n */\\n\\n/** @type {?HTMLSlotElement} */\\nNode.prototype.assignedSlot;\\n\\n/**\\n * @type {string}\\n * @see https://dom.spec.whatwg.org/#dom-element-slot\\n */\\nElement.prototype.slot;\\n\\n/**\\n * Note: In IE, the contains() method only exists on Elements, not Nodes.\\n * Therefore, it is recommended that you use the Conformance framework to\\n * prevent calling this on Nodes which are not Elements.\\n * @see https://connect.microsoft.com/IE/feedback/details/780874/node-contains-is-incorrect\\n *\\n * @param {Node} n The node to check\\n * @return {boolean} If \'n\' is this Node, or is contained within this Node.\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Node.contains\\n * @nosideeffects\\n */\\nNode.prototype.contains = function(n) {};\\n\\n/** @type {boolean} */\\nNode.prototype.isConnected;\\n\\n/**\\n * @type {boolean}\\n * @see https://html.spec.whatwg.org/multipage/scripting.html#the-script-element\\n */\\nHTMLScriptElement.prototype.async;\\n\\n/**\\n * @constructor\\n * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#the-canvas-element\\n * @extends {HTMLElement}\\n */\\nfunction HTMLCanvasElement() {}\\n\\n/** @type {number} */\\nHTMLCanvasElement.prototype.width;\\n\\n/** @type {number} */\\nHTMLCanvasElement.prototype.height;\\n\\n/**\\n * @see https://www.w3.org/TR/html5/scripting-1.html#dom-canvas-toblob\\n * @param {function(!Blob)} callback\\n * @param {string=} opt_type\\n * @param {...*} var_args\\n * @throws {Error}\\n */\\nHTMLCanvasElement.prototype.toBlob = function(callback, opt_type, var_args) {};\\n\\n/**\\n * @param {string=} opt_type\\n * @param {...*} var_args\\n * @return {string}\\n * @throws {Error}\\n */\\nHTMLCanvasElement.prototype.toDataURL = function(opt_type, var_args) {};\\n\\n/**\\n * @param {string} contextId\\n * @param {Object=} opt_args\\n * @return {Object}\\n */\\nHTMLCanvasElement.prototype.getContext = function(contextId, opt_args) {};\\n\\n/**\\n * @see https://www.w3.org/TR/mediacapture-fromelement/\\n * @param {number=} opt_framerate\\n * @return {!MediaStream}\\n * @throws {Error}\\n * */\\nHTMLCanvasElement.prototype.captureStream = function(opt_framerate) {};\\n\\n/**\\n * @see https://html.spec.whatwg.org/multipage/canvas.html#the-offscreencanvas-interface\\n * @implements {EventTarget}\\n * @implements {Transferable}\\n * @param {number} width\\n * @param {number} height\\n * @constructor\\n */\\nfunction OffscreenCanvas(width, height) {}\\n\\n/** @override */\\nOffscreenCanvas.prototype.addEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nOffscreenCanvas.prototype.removeEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nOffscreenCanvas.prototype.dispatchEvent = function(evt) {};\\n\\n/** @type {number} */\\nOffscreenCanvas.prototype.width;\\n\\n/** @type {number} */\\nOffscreenCanvas.prototype.height;\\n\\n/**\\n * @param {string} contextId\\n * @param {!Object=} opt_options\\n * @return {!Object}\\n */\\nOffscreenCanvas.prototype.getContext = function(contextId, opt_options) {};\\n\\n/**\\n * @return {!ImageBitmap}\\n */\\nOffscreenCanvas.prototype.transferToImageBitmap = function() {};\\n\\n/**\\n * @param {{type: (string|undefined), quality: (number|undefined)}=} opt_options\\n * @return {!Promise}\\n */\\nOffscreenCanvas.prototype.convertToBlob = function(opt_options) {};\\n\\n// TODO(tjgq): Find a way to add SVGImageElement to this typedef without making\\n// svg.js part of core.\\n/**\\n * @typedef {HTMLImageElement|HTMLVideoElement|HTMLCanvasElement|ImageBitmap|\\n * OffscreenCanvas}\\n */\\nvar CanvasImageSource;\\n\\n/**\\n * @interface\\n * @see https://www.w3.org/TR/2dcontext/#canvaspathmethods\\n */\\nfunction CanvasPathMethods() {}\\n\\n/**\\n * @return {undefined}\\n */\\nCanvasPathMethods.prototype.closePath = function() {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @return {undefined}\\n */\\nCanvasPathMethods.prototype.moveTo = function(x, y) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @return {undefined}\\n */\\nCanvasPathMethods.prototype.lineTo = function(x, y) {};\\n\\n/**\\n * @param {number} cpx\\n * @param {number} cpy\\n * @param {number} x\\n * @param {number} y\\n * @return {undefined}\\n */\\nCanvasPathMethods.prototype.quadraticCurveTo = function(cpx, cpy, x, y) {};\\n\\n/**\\n * @param {number} cp1x\\n * @param {number} cp1y\\n * @param {number} cp2x\\n * @param {number} cp2y\\n * @param {number} x\\n * @param {number} y\\n * @return {undefined}\\n */\\nCanvasPathMethods.prototype.bezierCurveTo = function(\\n cp1x, cp1y, cp2x, cp2y, x, y) {};\\n\\n/**\\n * @param {number} x1\\n * @param {number} y1\\n * @param {number} x2\\n * @param {number} y2\\n * @param {number} radius\\n * @return {undefined}\\n */\\nCanvasPathMethods.prototype.arcTo = function(x1, y1, x2, y2, radius) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @param {number} w\\n * @param {number} h\\n * @return {undefined}\\n */\\nCanvasPathMethods.prototype.rect = function(x, y, w, h) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @param {number} radius\\n * @param {number} startAngle\\n * @param {number} endAngle\\n * @param {boolean=} opt_anticlockwise\\n * @return {undefined}\\n */\\nCanvasPathMethods.prototype.arc = function(\\n x, y, radius, startAngle, endAngle, opt_anticlockwise) {};\\n\\n/**\\n * @constructor\\n * @implements {CanvasPathMethods}\\n * @see https://html.spec.whatwg.org/multipage/scripting.html#path2d-objects\\n */\\nfunction Path2D() {}\\n\\n/**\\n * @return {undefined}\\n * @override\\n */\\nPath2D.prototype.closePath = function() {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @return {undefined}\\n * @override\\n */\\nPath2D.prototype.moveTo = function(x, y) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @return {undefined}\\n * @override\\n */\\nPath2D.prototype.lineTo = function(x, y) {};\\n\\n/**\\n * @param {number} cpx\\n * @param {number} cpy\\n * @param {number} x\\n * @param {number} y\\n * @return {undefined}\\n * @override\\n */\\nPath2D.prototype.quadraticCurveTo = function(cpx, cpy, x, y) {};\\n\\n/**\\n * @param {number} cp1x\\n * @param {number} cp1y\\n * @param {number} cp2x\\n * @param {number} cp2y\\n * @param {number} x\\n * @param {number} y\\n * @return {undefined}\\n * @override\\n */\\nPath2D.prototype.bezierCurveTo = function(\\n cp1x, cp1y, cp2x, cp2y, x, y) {};\\n\\n/**\\n * @param {number} x1\\n * @param {number} y1\\n * @param {number} x2\\n * @param {number} y2\\n * @param {number} radius\\n * @return {undefined}\\n * @override\\n */\\nPath2D.prototype.arcTo = function(x1, y1, x2, y2, radius) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @param {number} w\\n * @param {number} h\\n * @return {undefined}\\n * @override\\n */\\nPath2D.prototype.rect = function(x, y, w, h) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @param {number} radius\\n * @param {number} startAngle\\n * @param {number} endAngle\\n * @param {boolean=} optAnticlockwise\\n * @return {undefined}\\n * @override\\n */\\nPath2D.prototype.arc = function(\\n x, y, radius, startAngle, endAngle, optAnticlockwise) {};\\n\\n/**\\n * @param {Path2D} path\\n * @return {undefined}\\n */\\nPath2D.prototype.addPath = function(path) {};\\n\\n/**\\n * @interface\\n * @see https://www.w3.org/TR/2dcontext/#canvasdrawingstyles\\n */\\nfunction CanvasDrawingStyles() {}\\n\\n/** @type {number} */\\nCanvasDrawingStyles.prototype.lineWidth;\\n\\n/** @type {string} */\\nCanvasDrawingStyles.prototype.lineCap;\\n\\n/** @type {string} */\\nCanvasDrawingStyles.prototype.lineJoin;\\n\\n/** @type {number} */\\nCanvasDrawingStyles.prototype.miterLimit;\\n\\n/**\\n * @param {Array} segments\\n * @return {undefined}\\n */\\nCanvasDrawingStyles.prototype.setLineDash;\\n\\n/**\\n * @return {!Array}\\n */\\nCanvasDrawingStyles.prototype.getLineDash;\\n\\n/** @type {string} */\\nCanvasDrawingStyles.prototype.font;\\n\\n/** @type {string} */\\nCanvasDrawingStyles.prototype.textAlign;\\n\\n/** @type {string} */\\nCanvasDrawingStyles.prototype.textBasel'; +a.a+="ine;\\n\\n/**\\n * @constructor\\n * @implements {CanvasDrawingStyles}\\n * @implements {CanvasPathMethods}\\n * @see http://www.w3.org/TR/2dcontext/#canvasrenderingcontext2d\\n */\\nfunction CanvasRenderingContext2D() {}\\n\\n/** @const {!HTMLCanvasElement} */\\nCanvasRenderingContext2D.prototype.canvas;\\n\\n/**\\n * @return {undefined}\\n */\\nCanvasRenderingContext2D.prototype.save = function() {};\\n\\n/**\\n * @return {undefined}\\n */\\nCanvasRenderingContext2D.prototype.restore = function() {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @return {undefined}\\n */\\nCanvasRenderingContext2D.prototype.scale = function(x, y) {};\\n\\n/**\\n * @param {number} angle\\n * @return {undefined}\\n */\\nCanvasRenderingContext2D.prototype.rotate = function(angle) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @return {undefined}\\n */\\nCanvasRenderingContext2D.prototype.translate = function(x, y) {};\\n\\n/**\\n * @param {number} m11\\n * @param {number} m12\\n * @param {number} m21\\n * @param {number} m22\\n * @param {number} dx\\n * @param {number} dy\\n * @return {undefined}\\n */\\nCanvasRenderingContext2D.prototype.transform = function(\\n m11, m12, m21, m22, dx, dy) {};\\n\\n/**\\n * @param {number} m11\\n * @param {number} m12\\n * @param {number} m21\\n * @param {number} m22\\n * @param {number} dx\\n * @param {number} dy\\n * @return {undefined}\\n */\\nCanvasRenderingContext2D.prototype.setTransform = function(\\n m11, m12, m21, m22, dx, dy) {};\\n\\n/**\\n * @param {number} x0\\n * @param {number} y0\\n * @param {number} x1\\n * @param {number} y1\\n * @return {!CanvasGradient}\\n * @throws {Error}\\n */\\nCanvasRenderingContext2D.prototype.createLinearGradient = function(\\n x0, y0, x1, y1) {};\\n\\n/**\\n * @param {number} x0\\n * @param {number} y0\\n * @param {number} r0\\n * @param {number} x1\\n * @param {number} y1\\n * @param {number} r1\\n * @return {!CanvasGradient}\\n * @throws {Error}\\n */\\nCanvasRenderingContext2D.prototype.createRadialGradient = function(\\n x0, y0, r0, x1, y1, r1) {};\\n\\n/**\\n * @param {CanvasImageSource} image\\n * @param {string} repetition\\n * @return {?CanvasPattern}\\n * @throws {Error}\\n * @see https://html.spec.whatwg.org/multipage/scripting.html#dom-context-2d-createpattern\\n */\\nCanvasRenderingContext2D.prototype.createPattern = function(\\n image, repetition) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @param {number} w\\n * @param {number} h\\n * @return {undefined}\\n */\\nCanvasRenderingContext2D.prototype.clearRect = function(x, y, w, h) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @param {number} w\\n * @param {number} h\\n * @return {undefined}\\n */\\nCanvasRenderingContext2D.prototype.fillRect = function(x, y, w, h) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @param {number} w\\n * @param {number} h\\n * @return {undefined}\\n */\\nCanvasRenderingContext2D.prototype.strokeRect = function(x, y, w, h) {};\\n\\n/**\\n * @return {undefined}\\n */\\nCanvasRenderingContext2D.prototype.beginPath = function() {};\\n\\n/**\\n * @return {undefined}\\n * @override\\n */\\nCanvasRenderingContext2D.prototype.closePath = function() {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @return {undefined}\\n * @override\\n */\\nCanvasRenderingContext2D.prototype.moveTo = function(x, y) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @return {undefined}\\n * @override\\n */\\nCanvasRenderingContext2D.prototype.lineTo = function(x, y) {};\\n\\n/**\\n * @param {number} cpx\\n * @param {number} cpy\\n * @param {number} x\\n * @param {number} y\\n * @return {undefined}\\n * @override\\n */\\nCanvasRenderingContext2D.prototype.quadraticCurveTo = function(\\n cpx, cpy, x, y) {};\\n\\n/**\\n * @param {number} cp1x\\n * @param {number} cp1y\\n * @param {number} cp2x\\n * @param {number} cp2y\\n * @param {number} x\\n * @param {number} y\\n * @return {undefined}\\n * @override\\n */\\nCanvasRenderingContext2D.prototype.bezierCurveTo = function(\\n cp1x, cp1y, cp2x, cp2y, x, y) {};\\n\\n/**\\n * @param {number} x1\\n * @param {number} y1\\n * @param {number} x2\\n * @param {number} y2\\n * @param {number} radius\\n * @return {undefined}\\n * @override\\n */\\nCanvasRenderingContext2D.prototype.arcTo = function(x1, y1, x2, y2, radius) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @param {number} w\\n * @param {number} h\\n * @return {undefined}\\n * @override\\n */\\nCanvasRenderingContext2D.prototype.rect = function(x, y, w, h) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @param {number} radius\\n * @param {number} startAngle\\n * @param {number} endAngle\\n * @param {boolean=} opt_anticlockwise\\n * @return {undefined}\\n * @override\\n */\\nCanvasRenderingContext2D.prototype.arc = function(\\n x, y, radius, startAngle, endAngle, opt_anticlockwise) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @param {number} radiusX\\n * @param {number} radiusY\\n * @param {number} rotation\\n * @param {number} startAngle\\n * @param {number} endAngle\\n * @param {boolean=} opt_anticlockwise\\n * @return {undefined}\\n * @see http://developer.mozilla.org/en/docs/Web/API/CanvasRenderingContext2D/ellipse\\n */\\nCanvasRenderingContext2D.prototype.ellipse = function(\\n x, y, radiusX, radiusY, rotation, startAngle, endAngle, opt_anticlockwise) {\\n};\\n\\n/**\\n * @param {Path2D|string=} optFillRuleOrPath\\n * @param {string=} optFillRule\\n * @return {undefined}\\n */\\nCanvasRenderingContext2D.prototype.fill = function(optFillRuleOrPath, optFillRule) {};\\n\\n/**\\n * @param {Path2D=} optStroke\\n * @return {undefined}\\n */\\nCanvasRenderingContext2D.prototype.stroke = function(optStroke) {};\\n\\n/**\\n * @param {Element} element\\n * @return {undefined}\\n */\\nCanvasRenderingContext2D.prototype.drawFocusIfNeeded = function(element) {};\\n\\n/**\\n * @param {Path2D|string=} optFillRuleOrPath\\n * @param {string=} optFillRule\\n * @return {undefined}\\n */\\nCanvasRenderingContext2D.prototype.clip = function(optFillRuleOrPath, optFillRule) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @return {boolean}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/isPointInStroke\\n */\\nCanvasRenderingContext2D.prototype.isPointInStroke = function(x, y) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @param {string=} opt_fillRule\\n * @return {boolean}\\n * @nosideeffects\\n */\\nCanvasRenderingContext2D.prototype.isPointInPath = function(\\n x, y, opt_fillRule) {};\\n\\n/**\\n * @param {string} text\\n * @param {number} x\\n * @param {number} y\\n * @param {number=} opt_maxWidth\\n * @return {undefined}\\n */\\nCanvasRenderingContext2D.prototype.fillText = function(\\n text, x, y, opt_maxWidth) {};\\n\\n/**\\n * @param {string} text\\n * @param {number} x\\n * @param {number} y\\n * @param {number=} opt_maxWidth\\n * @return {undefined}\\n */\\nCanvasRenderingContext2D.prototype.strokeText = function(\\n text, x, y, opt_maxWidth) {};\\n\\n/**\\n * @param {string} text\\n * @return {!TextMetrics}\\n * @nosideeffects\\n */\\nCanvasRenderingContext2D.prototype.measureText = function(text) {};\\n\\n/**\\n * @param {CanvasImageSource} image\\n * @param {number} dx Destination x coordinate.\\n * @param {number} dy Destination y coordinate.\\n * @param {number=} opt_dw Destination box width. Defaults to the image width.\\n * @param {number=} opt_dh Destination box height.\\n * Defaults to the image height.\\n * @param {number=} opt_sx Source box x coordinate. Used to select a portion of\\n * the source image to draw. Defaults to 0.\\n * @param {number=} opt_sy Source box y coordinate. Used to select a portion of\\n * the source image to draw. Defaults to 0.\\n * @param {number=} opt_sw Source box width. Used to select a portion of\\n * the source image to draw. Defaults to the full image width.\\n * @param {number=} opt_sh Source box height. Used to select a portion of\\n * the source image to draw. Defaults to the full image height.\\n * @return {undefined}\\n */\\nCanvasRenderingContext2D.prototype.drawImage = function(\\n image, dx, dy, opt_dw, opt_dh, opt_sx, opt_sy, opt_sw, opt_sh) {};\\n\\n/**\\n * @param {number} sw\\n * @param {number} sh\\n * @return {!ImageData}\\n * @throws {Error}\\n * @nosideeffects\\n */\\nCanvasRenderingContext2D.prototype.createImageData = function(sw, sh) {};\\n\\n/**\\n * @param {number} sx\\n * @param {number} sy\\n * @param {number} sw\\n * @param {number} sh\\n * @return {!ImageData}\\n * @throws {Error}\\n */\\nCanvasRenderingContext2D.prototype.getImageData = function(sx, sy, sw, sh) {};\\n\\n/**\\n * @param {ImageData} imagedata\\n * @param {number} dx\\n * @param {number} dy\\n * @param {number=} opt_dirtyX\\n * @param {number=} opt_dirtyY\\n * @param {number=} opt_dirtyWidth\\n * @param {number=} opt_dirtyHeight\\n * @return {undefined}\\n */\\nCanvasRenderingContext2D.prototype.putImageData = function(imagedata, dx, dy,\\n opt_dirtyX, opt_dirtyY, opt_dirtyWidth, opt_dirtyHeight) {};\\n\\n/**\\n * Note: WebKit only\\n * @param {number|string=} opt_a\\n * @param {number=} opt_b\\n * @param {number=} opt_c\\n * @param {number=} opt_d\\n * @param {number=} opt_e\\n * @see http://developer.apple.com/library/safari/#documentation/appleapplications/reference/WebKitDOMRef/CanvasRenderingContext2D_idl/Classes/CanvasRenderingContext2D/index.html\\n * @return {undefined}\\n */\\nCanvasRenderingContext2D.prototype.setFillColor;\\n\\n/**\\n * Note: WebKit only\\n * @param {number|string=} opt_a\\n * @param {number=} opt_b\\n * @param {number=} opt_c\\n * @param {number=} opt_d\\n * @param {number=} opt_e\\n * @see http://developer.apple.com/library/safari/#documentation/appleapplications/reference/WebKitDOMRef/CanvasRenderingContext2D_idl/Classes/CanvasRenderingContext2D/index.html\\n * @return {undefined}\\n */\\nCanvasRenderingContext2D.prototype.setStrokeColor;\\n\\n/**\\n * @return {!Array}\\n */\\nCanvasRenderingContext2D.prototype.getLineDash;\\n\\n/**\\n * @param {Array} segments\\n * @return {undefined}\\n */\\nCanvasRenderingContext2D.prototype.setLineDash;\\n\\n/** @type {string} */\\nCanvasRenderingContext2D.prototype.fillColor;\\n\\n/**\\n * @type {string|!CanvasGradient|!CanvasPattern}\\n * @see https://html.spec.whatwg.org/multipage/scripting.html#fill-and-stroke-styles:dom-context-2d-fillstyle\\n * @implicitCast\\n */\\nCanvasRenderingContext2D.prototype.fillStyle;\\n\\n/** @type {string} */\\nCanvasRenderingContext2D.prototype.font;\\n\\n/** @type {number} */\\nCanvasRenderingContext2D.prototype.globalAlpha;\\n\\n/** @type {string} */\\nCanvasRenderingContext2D.prototype.globalCompositeOperation;\\n\\n/** @type {number} */\\nCanvasRenderingContext2D.prototype.lineWidth;\\n\\n/** @type {string} */\\nCanvasRenderingContext2D.prototype.lineCap;\\n\\n/** @type {string} */\\nCanvasRenderingContext2D.prototype.lineJoin;\\n\\n/** @type {number} */\\nCanvasRenderingContext2D.prototype.miterLimit;\\n\\n/** @type {number} */\\nCanvasRenderingContext2D.prototype.shadowBlur;\\n\\n/** @type {string} */\\nCanvasRenderingContext2D.prototype.shadowColor;\\n\\n/** @type {number} */\\nCanvasRenderingContext2D.prototype.shadowOffsetX;\\n\\n/** @type {number} */\\nCanvasRenderingContext2D.prototype.shadowOffsetY;\\n\\n/**\\n * @type {string|!CanvasGradient|!CanvasPattern}\\n * @see https://html.spec.whatwg.org/multipage/scripting.html#fill-and-stroke-styles:dom-context-2d-strokestyle\\n * @implicitCast\\n */\\nCanvasRenderingContext2D.prototype.strokeStyle;\\n\\n/** @type {string} */\\nCanvasRenderingContext2D.prototype.strokeColor;\\n\\n/** @type {string} */\\nCanvasRenderingContext2D.prototype.textAlign;\\n\\n/** @type {string} */\\nCanvasRenderingContext2D.prototype.textBaseline;\\n\\n/** @type {number} */\\nCanvasRenderingContext2D.prototype.lineDashOffset;\\n\\n/**\\n * @constructor\\n */\\nfunction CanvasGradient() {}\\n\\n/**\\n * @param {number} offset\\n * @param {string} color\\n * @return {undefined}\\n */\\nCanvasGradient.prototype.addColorStop = function(offset, color) {};\\n\\n/**\\n * @constructor\\n */\\nfunction CanvasPattern() {}\\n\\n/**\\n * @constructor\\n */\\nfunction TextMetrics() {}\\n\\n/** @const {number} */\\nTextMetrics.prototype.width;\\n\\n/**\\n * @param {!Uint8ClampedArray|number} dataOrWidth In the first form, this is the\\n * array of pixel data. In the second form, this is the image width.\\n * @param {number} widthOrHeight In the first form, this is the image width. In\\n * the second form, this is the image height.\\n * @param {number=} opt_height In the first form, this is the optional image\\n * height. The second form omits this argument.\\n * @see https://html.spec.whatwg.org/multipage/scripting.html#imagedata\\n * @constructor\\n */\\nfunction ImageData(dataOrWidth, widthOrHeight, opt_height) {}\\n\\n/** @const {!Uint8ClampedArray} */\\nImageData.prototype.data;\\n\\n/** @const {number} */\\nImageData.prototype.width;\\n\\n/** @const {number} */\\nImageData.prototype.height;\\n\\n/**\\n * @see https://www.w3.org/TR/html51/webappapis.html#webappapis-images\\n * @interface\\n */\\nfunction ImageBitmap() {}\\n\\n/**\\n * @type {number}\\n * @const\\n */\\nImageBitmap.prototype.width;\\n\\n/**\\n * @type {number}\\n * @const\\n */\\nImageBitmap.prototype.height;\\n\\n/**\\n * @param {(!HTMLCanvasElement|!Blob|!HTMLVideoElement|!HTMLImageElement|!ImageBitmap|!CanvasRenderingContext2D|!ImageData)} image\\n * @param {number=} opt_sx\\n * @param {number=} opt_sy\\n * @param {number=} opt_sw\\n * @param {number=} opt_sh\\n * @return {!Promise}\\n * @see https://www.w3.org/TR/html51/webappapis.html#webappapis-images\\n */\\nfunction createImageBitmap(image, opt_sx, opt_sy, opt_sw, opt_sh) {}\\n\\n\\n/**\\n * @constructor\\n */\\nfunction ClientInformation() {}\\n\\n/** @type {boolean} */\\nClientInformation.prototype.onLine;\\n\\n/**\\n * @param {string} protocol\\n * @param {string} uri\\n * @param {string} title\\n * @return {undefined}\\n */\\nClientInformation.prototype.registerProtocolHandler = function(\\n protocol, uri, title) {};\\n\\n/**\\n * @param {string} mimeType\\n * @param {string} uri\\n * @param {string} title\\n * @return {undefined}\\n */\\nClientInformation.prototype.registerContentHandler = function(\\n mimeType, uri, title) {};\\n\\n// HTML5 Database objects\\n/**\\n * @constructor\\n */\\nfunction Database() {}\\n\\n/**\\n * @type {string}\\n */\\nDatabase.prototype.version;\\n\\n/**\\n * @param {function(!SQLTransaction) : void} callback\\n * @param {(function(!SQLError) : void)=} opt_errorCallback\\n * @param {Function=} opt_Callback\\n * @return {undefined}\\n */\\nDatabase.prototype.transaction = function(\\n callback, opt_errorCallback, opt_Callback) {};\\n\\n/**\\n * @param {function(!SQLTransaction) : void} callback\\n * @param {(function(!SQLError) : void)=} opt_errorCallback\\n * @param {Function=} opt_Callback\\n * @return {undefined}\\n */\\nDatabase.prototype.readTransaction = function(\\n callback, opt_errorCallback, opt_Callback) {};\\n\\n/**\\n * @param {string} oldVersion\\n * @param {string} newVersion\\n * @param {function(!SQLTransaction) : void} callback\\n * @param {function(!SQLError) : void} errorCallback\\n * @param {Function} successCallback\\n * @return {undefined}\\n */\\nDatabase.prototype.changeVersion = function(\\n oldVersion, newVersion, callback, errorCallback, successCallback) {};\\n\\n/**\\n * @interface\\n */\\nfunction DatabaseCallback() {}\\n\\n/**\\n * @param {!Database} db\\n * @return {undefined}\\n */\\nDatabaseCallback.prototype.handleEvent = function(db) {};\\n\\n/**\\n * @constructor\\n */\\nfunction SQLError() {}\\n\\n/**\\n * @type {number}\\n */\\nSQLError.prototype.code;\\n\\n/**\\n * @type {string}\\n */\\nSQLError.prototype.message;\\n\\n/**\\n * @constructor\\n */\\nfunction SQLTransaction() {}\\n\\n/**\\n * @param {string} sqlStatement\\n * @param {Array<*>=} opt_queryArgs\\n * @param {SQLStatementCallback=} opt_callback\\n * @param {(function(!SQLTransaction, !SQLError) : (boolean|void))=}\\n * opt_errorCallback\\n * @return {undefined}\\n */\\nSQLTransaction.prototype.executeSql = function(\\n sqlStatement, opt_queryArgs, opt_callback, opt_errorCallback) {};\\n\\n/**\\n * @typedef {(function(!SQLTransaction, !SQLResultSet) : void)}\\n */\\nvar SQLStatementCallback;\\n\\n/**\\n * @constructor\\n */\\nfunction SQLResultSet() {}\\n\\n/**\\n * @type {number}\\n */\\nSQLResultSet.prototype.insertId;\\n\\n/**\\n * @type {number}\\n */\\nSQLResultSet.prototype.rowsAffected;\\n\\n/**\\n * @type {!SQLResultSetRowList}\\n */\\nSQLResultSet.prototype.rows;\\n\\n/**\\n * @constructor\\n * @implements {IArrayLike}\\n * @see http://www.w3.org/TR/webdatabase/#sqlresultsetrowlist\\n */\\n"; +a.a+="function SQLResultSetRowList() {}\\n\\n/**\\n * @type {number}\\n */\\nSQLResultSetRowList.prototype.length;\\n\\n/**\\n * @param {number} index\\n * @return {Object}\\n * @nosideeffects\\n */\\nSQLResultSetRowList.prototype.item = function(index) {};\\n\\n/**\\n * @param {string} name\\n * @param {string} version\\n * @param {string} description\\n * @param {number} size\\n * @param {(DatabaseCallback|function(Database))=} opt_callback\\n * @return {!Database}\\n */\\nfunction openDatabase(name, version, description, size, opt_callback) {}\\n\\n/**\\n * @param {string} name\\n * @param {string} version\\n * @param {string} description\\n * @param {number} size\\n * @param {(DatabaseCallback|function(Database))=} opt_callback\\n * @return {!Database}\\n */\\nWindow.prototype.openDatabase =\\n function(name, version, description, size, opt_callback) {};\\n\\n/**\\n * @type {boolean}\\n * @see https://www.w3.org/TR/html5/embedded-content-0.html#dom-img-complete\\n */\\nHTMLImageElement.prototype.complete;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/html5/embedded-content-0.html#dom-img-naturalwidth\\n */\\nHTMLImageElement.prototype.naturalWidth;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/html5/embedded-content-0.html#dom-img-naturalheight\\n */\\nHTMLImageElement.prototype.naturalHeight;\\n\\n/**\\n * @type {string}\\n * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/embedded-content-1.html#attr-img-crossorigin\\n */\\nHTMLImageElement.prototype.crossOrigin;\\n\\n/**\\n * @type {string}\\n * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-currentsrc\\n */\\nHTMLImageElement.prototype.currentSrc;\\n\\n/**\\n * @type {string}\\n * @see https://html.spec.whatwg.org/multipage/images.html#image-decoding-hint\\n */\\nHTMLImageElement.prototype.decoding;\\n\\n/**\\n * @return {!Promise}\\n * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-decode\\n */\\nHTMLImageElement.prototype.decode;\\n\\n\\n/**\\n * This is a superposition of the Window and Worker postMessage methods.\\n * @param {*} message\\n * @param {(string|!Array)=} opt_targetOriginOrTransfer\\n * @param {(string|!Array|!Array)=}\\n * opt_targetOriginOrPortsOrTransfer\\n * @return {void}\\n */\\nfunction postMessage(message, opt_targetOriginOrTransfer,\\n opt_targetOriginOrPortsOrTransfer) {}\\n\\n/**\\n * The postMessage method (as implemented in Opera).\\n * @param {string} message\\n */\\nDocument.prototype.postMessage = function(message) {};\\n\\n/**\\n * Document head accessor.\\n * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#the-head-element-0\\n * @type {HTMLHeadElement}\\n */\\nDocument.prototype.head;\\n\\n/**\\n * @return {?Selection}\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/DocumentOrShadowRoot/getSelection\\n * @nosideeffects\\n */\\nDocument.prototype.getSelection = function() {};\\n\\n/**\\n * @type {string}\\n * @see https://html.spec.whatwg.org/multipage/dom.html#current-document-readiness\\n */\\nDocument.prototype.readyState;\\n\\n/**\\n * @see https://developer.apple.com/webapps/docs/documentation/AppleApplications/Reference/SafariJSRef/DOMApplicationCache/DOMApplicationCache.html\\n * @constructor\\n * @implements {EventTarget}\\n */\\nfunction DOMApplicationCache() {}\\n\\n/** @override */\\nDOMApplicationCache.prototype.addEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nDOMApplicationCache.prototype.removeEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nDOMApplicationCache.prototype.dispatchEvent = function(evt) {};\\n\\n/**\\n * The object isn't associated with an application cache. This can occur if the\\n * update process fails and there is no previous cache to revert to, or if there\\n * is no manifest file.\\n * @type {number}\\n */\\nDOMApplicationCache.prototype.UNCACHED = 0;\\n\\n/**\\n * The cache is idle.\\n * @type {number}\\n */\\nDOMApplicationCache.prototype.IDLE = 1;\\n\\n/**\\n * The update has started but the resources are not downloaded yet - for\\n * example, this can happen when the manifest file is fetched.\\n * @type {number}\\n */\\nDOMApplicationCache.prototype.CHECKING = 2;\\n\\n/**\\n * The resources are being downloaded into the cache.\\n * @type {number}\\n */\\nDOMApplicationCache.prototype.DOWNLOADING = 3;\\n\\n/**\\n * Resources have finished downloading and the new cache is ready to be used.\\n * @type {number}\\n */\\nDOMApplicationCache.prototype.UPDATEREADY = 4;\\n\\n/**\\n * The cache is obsolete.\\n * @type {number}\\n */\\nDOMApplicationCache.prototype.OBSOLETE = 5;\\n\\n/**\\n * The current status of the application cache.\\n * @type {number}\\n */\\nDOMApplicationCache.prototype.status;\\n\\n/**\\n * Sent when the update process finishes for the first time; that is, the first\\n * time an application cache is saved.\\n * @type {?function(!Event)}\\n */\\nDOMApplicationCache.prototype.oncached;\\n\\n/**\\n * Sent when the cache update process begins.\\n * @type {?function(!Event)}\\n */\\nDOMApplicationCache.prototype.onchecking;\\n\\n/**\\n * Sent when the update process begins downloading resources in the manifest\\n * file.\\n * @type {?function(!Event)}\\n */\\nDOMApplicationCache.prototype.ondownloading;\\n\\n/**\\n * Sent when an error occurs.\\n * @type {?function(!Event)}\\n */\\nDOMApplicationCache.prototype.onerror;\\n\\n/**\\n * Sent when the update process finishes but the manifest file does not change.\\n * @type {?function(!Event)}\\n */\\nDOMApplicationCache.prototype.onnoupdate;\\n\\n/**\\n * Sent when each resource in the manifest file begins to download.\\n * @type {?function(!Event)}\\n */\\nDOMApplicationCache.prototype.onprogress;\\n\\n/**\\n * Sent when there is an existing application cache, the update process\\n * finishes, and there is a new application cache ready for use.\\n * @type {?function(!Event)}\\n */\\nDOMApplicationCache.prototype.onupdateready;\\n\\n/**\\n * Replaces the active cache with the latest version.\\n * @throws {DOMException}\\n * @return {undefined}\\n */\\nDOMApplicationCache.prototype.swapCache = function() {};\\n\\n/**\\n * Manually triggers the update process.\\n * @throws {DOMException}\\n * @return {undefined}\\n */\\nDOMApplicationCache.prototype.update = function() {};\\n\\n/** @type {DOMApplicationCache} */\\nvar applicationCache;\\n\\n/** @type {DOMApplicationCache} */\\nWindow.prototype.applicationCache;\\n\\n/**\\n * @see https://developer.mozilla.org/En/DOM/Worker/Functions_available_to_workers\\n * @param {...!TrustedScriptURL|string} var_args\\n * @return {undefined}\\n */\\nWindow.prototype.importScripts = function(var_args) {};\\n\\n/**\\n * @see https://developer.mozilla.org/En/DOM/Worker/Functions_available_to_workers\\n * @param {...!TrustedScriptURL|string} var_args\\n * @return {undefined}\\n */\\nfunction importScripts(var_args) {}\\n\\n/**\\n * @see http://dev.w3.org/html5/workers/\\n * @constructor\\n * @implements {EventTarget}\\n */\\nfunction WebWorker() {}\\n\\n/** @override */\\nWebWorker.prototype.addEventListener = function(type, listener, opt_options) {};\\n\\n/** @override */\\nWebWorker.prototype.removeEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nWebWorker.prototype.dispatchEvent = function(evt) {};\\n\\n/**\\n * Stops the worker process\\n * @return {undefined}\\n */\\nWebWorker.prototype.terminate = function() {};\\n\\n/**\\n * Posts a message to the worker thread.\\n * @param {string} message\\n * @return {undefined}\\n */\\nWebWorker.prototype.postMessage = function(message) {};\\n\\n/**\\n * Sent when the worker thread posts a message to its creator.\\n * @type {?function(!MessageEvent<*>): void}\\n */\\nWebWorker.prototype.onmessage;\\n\\n/**\\n * Sent when the worker thread encounters an error.\\n * @type {?function(!ErrorEvent): void}\\n */\\nWebWorker.prototype.onerror;\\n\\n/**\\n * @see http://dev.w3.org/html5/workers/\\n * @param {!string} scriptURL\\n * @param {!WorkerOptions=} opt_options\\n * @constructor\\n * @implements {EventTarget}\\n */\\nfunction Worker(scriptURL, opt_options) {}\\n\\n/** @override */\\nWorker.prototype.addEventListener = function(type, listener, opt_options) {};\\n\\n/** @override */\\nWorker.prototype.removeEventListener = function(type, listener, opt_options) {};\\n\\n/** @override */\\nWorker.prototype.dispatchEvent = function(evt) {};\\n\\n/**\\n * Stops the worker process\\n * @return {undefined}\\n */\\nWorker.prototype.terminate = function() {};\\n\\n/**\\n * Posts a message to the worker thread.\\n * @param {*} message\\n * @param {Array=} opt_transfer\\n * @return {undefined}\\n */\\nWorker.prototype.postMessage = function(message, opt_transfer) {};\\n\\n/**\\n * Posts a message to the worker thread.\\n * @param {*} message\\n * @param {Array=} opt_transfer\\n * @return {undefined}\\n */\\nWorker.prototype.webkitPostMessage = function(message, opt_transfer) {};\\n\\n/**\\n * Sent when the worker thread posts a message to its creator.\\n * @type {?function(!MessageEvent<*>): void}\\n */\\nWorker.prototype.onmessage;\\n\\n/**\\n * Sent when the worker thread encounters an error.\\n * @type {?function(!ErrorEvent): void}\\n */\\nWorker.prototype.onerror;\\n\\n/**\\n * @see http://dev.w3.org/html5/workers/\\n * @record\\n */\\nfunction WorkerOptions() {}\\n\\n/**\\n * Defines a name for the new global environment of the worker, primarily for\\n * debugging purposes.\\n * @type {string|undefined}\\n */\\nWorkerOptions.prototype.name;\\n\\n/**\\n * 'classic' or 'module'. Default: 'classic'.\\n * Specifying 'module' ensures the worker environment supports JavaScript\\n * modules.\\n * @type {string|undefined}\\n */\\nWorkerOptions.prototype.type;\\n\\n// WorkerOptions.prototype.credentials is defined in fetchapi.js.\\n// if type = 'module', it specifies how scriptURL is fetched.\\n\\n/**\\n * @see http://dev.w3.org/html5/workers/\\n * @param {string} scriptURL The URL of the script to run in the SharedWorker.\\n * @param {string=} opt_name A name that can later be used to obtain a\\n * reference to the same SharedWorker.\\n * @constructor\\n * @implements {EventTarget}\\n */\\nfunction SharedWorker(scriptURL, opt_name) {}\\n\\n/** @override */\\nSharedWorker.prototype.addEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nSharedWorker.prototype.removeEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nSharedWorker.prototype.dispatchEvent = function(evt) {};\\n\\n/**\\n * @type {!MessagePort}\\n */\\nSharedWorker.prototype.port;\\n\\n/**\\n * Called on network errors for loading the initial script.\\n * @type {?function(!ErrorEvent): void}\\n */\\nSharedWorker.prototype.onerror;\\n\\n/**\\n * @see http://dev.w3.org/html5/workers/\\n * @see http://www.w3.org/TR/url-1/#dom-urlutilsreadonly\\n * @interface\\n */\\nfunction WorkerLocation() {}\\n\\n/** @type {string} */\\nWorkerLocation.prototype.href;\\n\\n/** @type {string} */\\nWorkerLocation.prototype.origin;\\n\\n/** @type {string} */\\nWorkerLocation.prototype.protocol;\\n\\n/** @type {string} */\\nWorkerLocation.prototype.host;\\n\\n/** @type {string} */\\nWorkerLocation.prototype.hostname;\\n\\n/** @type {string} */\\nWorkerLocation.prototype.port;\\n\\n/** @type {string} */\\nWorkerLocation.prototype.pathname;\\n\\n/** @type {string} */\\nWorkerLocation.prototype.search;\\n\\n/** @type {string} */\\nWorkerLocation.prototype.hash;\\n\\n/**\\n * @see http://dev.w3.org/html5/workers/\\n * @interface\\n * @extends {EventTarget}\\n */\\nfunction WorkerGlobalScope() {}\\n\\n/** @type {!WorkerGlobalScope} */\\nWorkerGlobalScope.prototype.self;\\n\\n/** @type {!WorkerLocation} */\\nWorkerGlobalScope.prototype.location;\\n\\n/**\\n * Closes the worker represented by this WorkerGlobalScope.\\n * @return {undefined}\\n */\\nWorkerGlobalScope.prototype.close = function() {};\\n\\n/**\\n * Sent when the worker encounters an error.\\n * @type {?function(string, string, number, number, !Error): void}\\n */\\nWorkerGlobalScope.prototype.onerror;\\n\\n/**\\n * Sent when the worker goes offline.\\n * @type {?function(!Event): void}\\n */\\nWorkerGlobalScope.prototype.onoffline;\\n\\n/**\\n * Sent when the worker goes online.\\n * @type {?function(!Event): void}\\n */\\nWorkerGlobalScope.prototype.ononline;\\n\\n/** @type {!WorkerPerformance} */\\nWorkerGlobalScope.prototype.performance;\\n\\n/** @type {!WorkerNavigator} */\\nWorkerGlobalScope.prototype.navigator;\\n\\n/**\\n * @see http://dev.w3.org/html5/workers/\\n * @interface\\n * @extends {WorkerGlobalScope}\\n */\\nfunction DedicatedWorkerGlobalScope() {}\\n\\n/**\\n * Posts a message to creator of this worker.\\n * @param {*} message\\n * @param {Array=} opt_transfer\\n * @return {undefined}\\n */\\nDedicatedWorkerGlobalScope.prototype.postMessage =\\n function(message, opt_transfer) {};\\n\\n/**\\n * Posts a message to creator of this worker.\\n * @param {*} message\\n * @param {Array=} opt_transfer\\n * @return {undefined}\\n */\\nDedicatedWorkerGlobalScope.prototype.webkitPostMessage =\\n function(message, opt_transfer) {};\\n\\n/**\\n * Sent when the creator posts a message to this worker.\\n * @type {?function(!MessageEvent<*>): void}\\n */\\nDedicatedWorkerGlobalScope.prototype.onmessage;\\n\\n/**\\n * @see http://dev.w3.org/html5/workers/\\n * @interface\\n * @extends {WorkerGlobalScope}\\n */\\nfunction SharedWorkerGlobalScope() {}\\n\\n/** @type {string} */\\nSharedWorkerGlobalScope.prototype.name;\\n\\n/**\\n * Sent when a connection to this worker is opened.\\n * @type {?function(!Event)}\\n */\\nSharedWorkerGlobalScope.prototype.onconnect;\\n\\n/** @type {!Array|undefined} */\\nHTMLElement.observedAttributes;\\n\\n/**\\n * @param {!Document} oldDocument\\n * @param {!Document} newDocument\\n */\\nHTMLElement.prototype.adoptedCallback = function(oldDocument, newDocument) {};\\n\\n/**\\n * @param {!{mode: string}} options\\n * @return {!ShadowRoot}\\n */\\nHTMLElement.prototype.attachShadow = function(options) {};\\n\\n/**\\n * @param {string} attributeName\\n * @param {?string} oldValue\\n * @param {?string} newValue\\n * @param {?string} namespace\\n */\\nHTMLElement.prototype.attributeChangedCallback = function(attributeName, oldValue, newValue, namespace) {};\\n\\n/** @type {function()|undefined} */\\nHTMLElement.prototype.connectedCallback;\\n\\n/** @type {Element} */\\nHTMLElement.prototype.contextMenu;\\n\\n/** @type {function()|undefined} */\\nHTMLElement.prototype.disconnectedCallback;\\n\\n/** @type {boolean} */\\nHTMLElement.prototype.draggable;\\n\\n/**\\n * This is actually a DOMSettableTokenList property. However since that\\n * interface isn't currently defined and no known browsers implement this\\n * feature, just define the property for now.\\n *\\n * @const\\n * @type {Object}\\n */\\nHTMLElement.prototype.dropzone;\\n\\n/**\\n * @see http://www.w3.org/TR/html5/dom.html#dom-getelementsbyclassname\\n * @param {string} classNames\\n * @return {!NodeList}\\n * @nosideeffects\\n */\\nHTMLElement.prototype.getElementsByClassName = function(classNames) {};\\n// NOTE: Document.prototype.getElementsByClassName is in gecko_dom.js\\n\\n/** @type {boolean} */\\nHTMLElement.prototype.hidden;\\n\\n/** @type {boolean} */\\nHTMLElement.prototype.spellcheck;\\n\\n/**\\n * @see https://dom.spec.whatwg.org/#dictdef-getrootnodeoptions\\n * @typedef {{\\n * composed: boolean\\n * }}\\n */\\nvar GetRootNodeOptions;\\n\\n/**\\n * @see https://dom.spec.whatwg.org/#dom-node-getrootnode\\n * @param {GetRootNodeOptions=} opt_options\\n * @return {!Node}\\n */\\nNode.prototype.getRootNode = function(opt_options) {};\\n\\n/**\\n * @see http://www.w3.org/TR/components-intro/\\n * @return {!ShadowRoot}\\n */\\nHTMLElement.prototype.createShadowRoot;\\n\\n/**\\n * @see http://www.w3.org/TR/components-intro/\\n * @return {!ShadowRoot}\\n */\\nHTMLElement.prototype.webkitCreateShadowRoot;\\n\\n/**\\n * @see http://www.w3.org/TR/shadow-dom/\\n * @type {ShadowRoot}\\n */\\nHTMLElement.prototype.shadowRoot;\\n\\n/**\\n * @see http://www.w3.org/TR/shadow-dom/\\n * @return {!NodeList}\\n */\\nHTMLElement.prototype.getDestinationInsertionPoints = function() {};\\n\\n/**\\n * @see http://www.w3.org/TR/components-intro/\\n * @type {function()}\\n */\\nHTMLElement.prototype.createdCallback;\\n\\n/**\\n * @see http://w3c.github.io/webcomponents/explainer/#lifecycle-callbacks\\n * @type {function()}\\n */\\nHTMLElement.prototype.attachedCallback;\\n\\n/**\\n * @see http://w3c.github.io/webcomponents/explainer/#lifecycle-callbacks\\n * @type {function()}\\n */\\nHTMLElement.prototype.detachedCallback;\\n\\n/**\\n * Cryptographic nonce used by Content Security Policy.\\n * @see https://htm"; +a.a+="l.spec.whatwg.org/multipage/dom.html#elements-in-the-dom:noncedelement\\n * @type {?string}\\n */\\nHTMLElement.prototype.nonce;\\n\\n/** @type {string} */\\nHTMLAnchorElement.prototype.download;\\n\\n/** @type {string} */\\nHTMLAnchorElement.prototype.hash;\\n\\n/** @type {string} */\\nHTMLAnchorElement.prototype.host;\\n\\n/** @type {string} */\\nHTMLAnchorElement.prototype.hostname;\\n\\n/** @type {string} */\\nHTMLAnchorElement.prototype.pathname;\\n\\n/**\\n * The 'ping' attribute is known to be supported in recent versions (as of\\n * mid-2014) of Chrome, Safari, and Firefox, and is not supported in any\\n * current version of Internet Explorer.\\n *\\n * @type {string}\\n * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/semantics.html#hyperlink-auditing\\n */\\nHTMLAnchorElement.prototype.ping;\\n\\n/** @type {string} */\\nHTMLAnchorElement.prototype.port;\\n\\n/** @type {string} */\\nHTMLAnchorElement.prototype.protocol;\\n\\n/** @type {string} */\\nHTMLAnchorElement.prototype.search;\\n\\n/** @type {string} */\\nHTMLAreaElement.prototype.download;\\n\\n/**\\n * @type {string}\\n * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/semantics.html#hyperlink-auditing\\n */\\nHTMLAreaElement.prototype.ping;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/html-markup/iframe.html#iframe.attrs.srcdoc\\n */\\nHTMLIFrameElement.prototype.srcdoc;\\n\\n/**\\n * @type {?string}\\n * @see http://www.w3.org/TR/2012/WD-html5-20121025/the-iframe-element.html#attr-iframe-sandbox\\n */\\nHTMLIFrameElement.prototype.sandbox;\\n\\n/**\\n * @type {Window}\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/contentWindow\\n */\\nHTMLIFrameElement.prototype.contentWindow;\\n\\n/** @type {string} */\\nHTMLInputElement.prototype.autocomplete;\\n\\n/** @type {string} */\\nHTMLInputElement.prototype.dirname;\\n\\n/** @type {FileList} */\\nHTMLInputElement.prototype.files;\\n\\n/**\\n * @type {boolean}\\n * @see https://www.w3.org/TR/html5/forms.html#dom-input-indeterminate\\n */\\nHTMLInputElement.prototype.indeterminate;\\n\\n/** @type {string} */\\nHTMLInputElement.prototype.list;\\n\\n/** @implicitCast @type {string} */\\nHTMLInputElement.prototype.max;\\n\\n/** @implicitCast @type {string} */\\nHTMLInputElement.prototype.min;\\n\\n/** @type {string} */\\nHTMLInputElement.prototype.pattern;\\n\\n/** @type {boolean} */\\nHTMLInputElement.prototype.multiple;\\n\\n/** @type {string} */\\nHTMLInputElement.prototype.placeholder;\\n\\n/** @type {boolean} */\\nHTMLInputElement.prototype.required;\\n\\n/** @implicitCast @type {string} */\\nHTMLInputElement.prototype.step;\\n\\n/** @type {Date} */\\nHTMLInputElement.prototype.valueAsDate;\\n\\n/** @type {number} */\\nHTMLInputElement.prototype.valueAsNumber;\\n\\n/**\\n * Changes the form control's value by the value given in the step attribute\\n * multiplied by opt_n.\\n * @param {number=} opt_n step multiplier. Defaults to 1.\\n * @return {undefined}\\n */\\nHTMLInputElement.prototype.stepDown = function(opt_n) {};\\n\\n/**\\n * Changes the form control's value by the value given in the step attribute\\n * multiplied by opt_n.\\n * @param {number=} opt_n step multiplier. Defaults to 1.\\n * @return {undefined}\\n */\\nHTMLInputElement.prototype.stepUp = function(opt_n) {};\\n\\n\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement\\n */\\nfunction HTMLMediaElement() {}\\n\\n/** @const {number} */\\nHTMLMediaElement.NETWORK_EMPTY; // = 0\\n\\n/** @const {number} */\\nHTMLMediaElement.NETWORK_IDLE; // = 1\\n\\n/** @const {number} */\\nHTMLMediaElement.NETWORK_LOADING; // = 2\\n\\n/** @const {number} */\\nHTMLMediaElement.NETWORK_NO_SOURCE; // = 3\\n\\n/** @const {number} */\\nHTMLMediaElement.HAVE_NOTHING; // = 0\\n\\n/** @const {number} */\\nHTMLMediaElement.HAVE_METADATA; // = 1\\n\\n/** @const {number} */\\nHTMLMediaElement.HAVE_CURRENT_DATA; // = 2\\n\\n/** @const {number} */\\nHTMLMediaElement.HAVE_FUTURE_DATA; // = 3\\n\\n/** @const {number} */\\nHTMLMediaElement.HAVE_ENOUGH_DATA; // = 4\\n\\n/** @type {MediaError} */\\nHTMLMediaElement.prototype.error;\\n\\n/** @type {string} @implicitCast */\\nHTMLMediaElement.prototype.src;\\n\\n/** @type {string} */\\nHTMLMediaElement.prototype.currentSrc;\\n\\n/** @type {number} */\\nHTMLMediaElement.prototype.networkState;\\n\\n/** @type {boolean} */\\nHTMLMediaElement.prototype.autobuffer;\\n\\n/** @type {!TimeRanges} */\\nHTMLMediaElement.prototype.buffered;\\n\\n/** @type {?MediaStream} */\\nHTMLMediaElement.prototype.srcObject;\\n\\n/**\\n * Loads the media element.\\n * @return {undefined}\\n */\\nHTMLMediaElement.prototype.load = function() {};\\n\\n/**\\n * @param {string} type Type of the element in question in question.\\n * @return {string} Whether it can play the type.\\n * @nosideeffects\\n */\\nHTMLMediaElement.prototype.canPlayType = function(type) {};\\n\\n/** Event handlers */\\n\\n/** @type {?function(Event)} */\\nHTMLMediaElement.prototype.onabort;\\n\\n/** @type {?function(!Event)} */\\nHTMLMediaElement.prototype.oncanplay;\\n\\n/** @type {?function(!Event)} */\\nHTMLMediaElement.prototype.oncanplaythrough;\\n\\n/** @type {?function(!Event)} */\\nHTMLMediaElement.prototype.ondurationchange;\\n\\n/** @type {?function(!Event)} */\\nHTMLMediaElement.prototype.onemptied;\\n\\n/** @type {?function(!Event)} */\\nHTMLMediaElement.prototype.onended;\\n\\n/** @type {?function(Event)} */\\nHTMLMediaElement.prototype.onerror;\\n\\n/** @type {?function(!Event)} */\\nHTMLMediaElement.prototype.onloadeddata;\\n\\n/** @type {?function(!Event)} */\\nHTMLMediaElement.prototype.onloadedmetadata;\\n\\n/** @type {?function(!Event)} */\\nHTMLMediaElement.prototype.onloadstart;\\n\\n/** @type {?function(!Event)} */\\nHTMLMediaElement.prototype.onpause;\\n\\n/** @type {?function(!Event)} */\\nHTMLMediaElement.prototype.onplay;\\n\\n/** @type {?function(!Event)} */\\nHTMLMediaElement.prototype.onplaying;\\n\\n/** @type {?function(!Event)} */\\nHTMLMediaElement.prototype.onprogress;\\n\\n/** @type {?function(!Event)} */\\nHTMLMediaElement.prototype.onratechange;\\n\\n/** @type {?function(!Event)} */\\nHTMLMediaElement.prototype.onseeked;\\n\\n/** @type {?function(!Event)} */\\nHTMLMediaElement.prototype.onseeking;\\n\\n/** @type {?function(!Event)} */\\nHTMLMediaElement.prototype.onstalled;\\n\\n/** @type {?function(!Event)} */\\nHTMLMediaElement.prototype.onsuspend;\\n\\n/** @type {?function(!Event)} */\\nHTMLMediaElement.prototype.ontimeupdate;\\n\\n/** @type {?function(!Event)} */\\nHTMLMediaElement.prototype.onvolumechange;\\n\\n/** @type {?function(!Event)} */\\nHTMLMediaElement.prototype.onwaiting;\\n\\n/** @type {?function(Event)} */\\nHTMLImageElement.prototype.onload;\\n\\n/** @type {?function(Event)} */\\nHTMLImageElement.prototype.onerror;\\n\\n/** @type {string} */\\nHTMLMediaElement.prototype.preload;\\n\\n/** @type {number} */\\nHTMLMediaElement.prototype.readyState;\\n\\n/** @type {boolean} */\\nHTMLMediaElement.prototype.seeking;\\n\\n/**\\n * The current time, in seconds.\\n * @type {number}\\n */\\nHTMLMediaElement.prototype.currentTime;\\n\\n/**\\n * The absolute timeline offset.\\n * @return {!Date}\\n */\\nHTMLMediaElement.prototype.getStartDate = function() {};\\n\\n/**\\n * The length of the media in seconds.\\n * @type {number}\\n */\\nHTMLMediaElement.prototype.duration;\\n\\n/** @type {boolean} */\\nHTMLMediaElement.prototype.paused;\\n\\n/** @type {number} */\\nHTMLMediaElement.prototype.defaultPlaybackRate;\\n\\n/** @type {number} */\\nHTMLMediaElement.prototype.playbackRate;\\n\\n/** @type {TimeRanges} */\\nHTMLMediaElement.prototype.played;\\n\\n/** @type {TimeRanges} */\\nHTMLMediaElement.prototype.seekable;\\n\\n/** @type {boolean} */\\nHTMLMediaElement.prototype.ended;\\n\\n/** @type {boolean} */\\nHTMLMediaElement.prototype.autoplay;\\n\\n/** @type {boolean} */\\nHTMLMediaElement.prototype.loop;\\n\\n/**\\n * Starts playing the media.\\n * @return {?Promise} This is a *nullable* Promise on purpose unlike\\n * the HTML5 spec because supported older browsers (incl. Smart TVs) don't\\n * return a Promise.\\n */\\nHTMLMediaElement.prototype.play = function() {};\\n\\n/**\\n * Pauses the media.\\n * @return {undefined}\\n */\\nHTMLMediaElement.prototype.pause = function() {};\\n\\n/** @type {boolean} */\\nHTMLMediaElement.prototype.controls;\\n\\n/**\\n * The audio volume, from 0.0 (silent) to 1.0 (loudest).\\n * @type {number}\\n */\\nHTMLMediaElement.prototype.volume;\\n\\n/** @type {boolean} */\\nHTMLMediaElement.prototype.muted;\\n\\n/**\\n * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#dom-media-addtexttrack\\n * @param {string} kind Kind of the text track.\\n * @param {string=} opt_label Label of the text track.\\n * @param {string=} opt_language Language of the text track.\\n * @return {!TextTrack} TextTrack object added to the media element.\\n */\\nHTMLMediaElement.prototype.addTextTrack =\\n function(kind, opt_label, opt_language) {};\\n\\n/** @type {!TextTrackList} */\\nHTMLMediaElement.prototype.textTracks;\\n\\n/**\\n * The ID of the audio device through which output is being delivered, or an\\n * empty string if using the default device.\\n *\\n * Implemented as a draft spec in Chrome 49+.\\n *\\n * @see https://w3c.github.io/mediacapture-output/#htmlmediaelement-extensions\\n * @type {string}\\n */\\nHTMLMediaElement.prototype.sinkId;\\n\\n/**\\n * Sets the audio device through which output should be delivered.\\n *\\n * Implemented as a draft spec in Chrome 49+.\\n *\\n * @param {string} sinkId The ID of the audio output device, or empty string\\n * for default device.\\n *\\n * @see https://w3c.github.io/mediacapture-output/#htmlmediaelement-extensions\\n * @return {!Promise}\\n */\\nHTMLMediaElement.prototype.setSinkId = function(sinkId) {};\\n\\n\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see https://html.spec.whatwg.org/multipage/dom.html#htmlunknownelement\\n * @see https://html.spec.whatwg.org/multipage/scripting.html#customized-built-in-element-restrictions\\n * @see https://w3c.github.io/webcomponents/spec/custom/#custom-elements-api\\n */\\nfunction HTMLUnknownElement() {}\\n\\n\\n\\n/**\\n * @see http://www.w3.org/TR/shadow-dom/\\n * @return {!NodeList}\\n */\\nText.prototype.getDestinationInsertionPoints = function() {};\\n\\n\\n/**\\n * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#texttracklist\\n * @constructor\\n * @implements {IArrayLike}\\n */\\nfunction TextTrackList() {}\\n\\n/** @type {number} */\\nTextTrackList.prototype.length;\\n\\n/**\\n * @param {string} id\\n * @return {TextTrack}\\n */\\nTextTrackList.prototype.getTrackById = function(id) {};\\n\\n\\n/**\\n * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#texttrack\\n * @constructor\\n * @implements {EventTarget}\\n */\\nfunction TextTrack() {}\\n\\n/**\\n * @param {TextTrackCue} cue\\n * @return {undefined}\\n */\\nTextTrack.prototype.addCue = function(cue) {};\\n\\n/**\\n * @param {TextTrackCue} cue\\n * @return {undefined}\\n */\\nTextTrack.prototype.removeCue = function(cue) {};\\n\\n/**\\n * @const {TextTrackCueList}\\n */\\nTextTrack.prototype.activeCues;\\n\\n/**\\n * @const {TextTrackCueList}\\n */\\nTextTrack.prototype.cues;\\n\\n/**\\n * @type {string}\\n */\\nTextTrack.prototype.mode;\\n\\n/** @override */\\nTextTrack.prototype.addEventListener = function(\\n type, listener, opt_useCapture) {};\\n\\n/** @override */\\nTextTrack.prototype.dispatchEvent = function(evt) {};\\n\\n/** @override */\\nTextTrack.prototype.removeEventListener = function(\\n type, listener, opt_options) {};\\n\\n\\n\\n/**\\n * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#texttrackcuelist\\n * @constructor\\n * @implements {IArrayLike}\\n */\\nfunction TextTrackCueList() {}\\n\\n/** @const {number} */\\nTextTrackCueList.prototype.length;\\n\\n/**\\n * @param {string} id\\n * @return {TextTrackCue}\\n */\\nTextTrackCueList.prototype.getCueById = function(id) {};\\n\\n\\n\\n/**\\n * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#texttrackcue\\n * @constructor\\n * @param {number} startTime\\n * @param {number} endTime\\n * @param {string} text\\n */\\nfunction TextTrackCue(startTime, endTime, text) {}\\n\\n/** @type {string} */\\nTextTrackCue.prototype.id;\\n\\n/** @type {number} */\\nTextTrackCue.prototype.startTime;\\n\\n/** @type {number} */\\nTextTrackCue.prototype.endTime;\\n\\n/** @type {string} */\\nTextTrackCue.prototype.text;\\n\\n\\n\\n/**\\n * @see https://w3c.github.io/webvtt/#vttregion\\n * @constructor\\n */\\nfunction VTTRegion() {}\\n\\n/** @type {string} */\\nVTTRegion.prototype.id;\\n\\n/** @type {number} */\\nVTTRegion.prototype.width;\\n\\n/** @type {number} */\\nVTTRegion.prototype.lines;\\n\\n/** @type {number} */\\nVTTRegion.prototype.regionAnchorX;\\n\\n/** @type {number} */\\nVTTRegion.prototype.regionAnchorY;\\n\\n/** @type {number} */\\nVTTRegion.prototype.viewportAnchorX;\\n\\n/** @type {number} */\\nVTTRegion.prototype.viewportAnchorY;\\n\\n/**\\n * @see https://w3c.github.io/webvtt/#enumdef-scrollsetting\\n * @type {string}\\n */\\nVTTRegion.prototype.scroll;\\n\\n\\n\\n/**\\n * @see http://dev.w3.org/html5/webvtt/#the-vttcue-interface\\n * @constructor\\n * @extends {TextTrackCue}\\n * @param {number} startTime\\n * @param {number} endTime\\n * @param {string} text\\n */\\nfunction VTTCue(startTime, endTime, text) {}\\n\\n/** @type {?VTTRegion} */\\nVTTCue.prototype.region;\\n\\n/**\\n * @see https://w3c.github.io/webvtt/#enumdef-directionsetting\\n * @type {string}\\n */\\nVTTCue.prototype.vertical;\\n\\n/** @type {boolean} */\\nVTTCue.prototype.snapToLines;\\n\\n/** @type {(number|string)} */\\nVTTCue.prototype.line;\\n\\n/**\\n * @see https://w3c.github.io/webvtt/#enumdef-linealignsetting\\n * @type {string}\\n */\\nVTTCue.prototype.lineAlign;\\n\\n/** @type {(number|string)} */\\nVTTCue.prototype.position;\\n\\n/**\\n * @see https://w3c.github.io/webvtt/#enumdef-positionalignsetting\\n * @type {string}\\n */\\nVTTCue.prototype.positionAlign;\\n\\n/** @type {number} */\\nVTTCue.prototype.size;\\n\\n/**\\n * @see https://w3c.github.io/webvtt/#enumdef-alignsetting\\n * @type {string}\\n */\\nVTTCue.prototype.align;\\n\\n/** @type {string} */\\nVTTCue.prototype.text;\\n\\n/** @return {!DocumentFragment} */\\nVTTCue.prototype.getCueAsHTML = function() {};\\n\\n\\n/**\\n * @constructor\\n * @extends {HTMLMediaElement}\\n */\\nfunction HTMLAudioElement() {}\\n\\n/**\\n * @constructor\\n * @extends {HTMLMediaElement}\\n * The webkit-prefixed attributes are defined in\\n * https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/html/media/HTMLMediaElement.idl\\n */\\nfunction HTMLVideoElement() {}\\n\\n/**\\n * Starts displaying the video in full screen mode.\\n * @return {undefined}\\n */\\nHTMLVideoElement.prototype.webkitEnterFullscreen = function() {};\\n\\n/**\\n * Starts displaying the video in full screen mode.\\n * @return {undefined}\\n */\\nHTMLVideoElement.prototype.webkitEnterFullScreen = function() {};\\n\\n/**\\n * Stops displaying the video in full screen mode.\\n * @return {undefined}\\n */\\nHTMLVideoElement.prototype.webkitExitFullscreen = function() {};\\n\\n/**\\n * Stops displaying the video in full screen mode.\\n * @return {undefined}\\n */\\nHTMLVideoElement.prototype.webkitExitFullScreen = function() {};\\n\\n/** @type {number} */\\nHTMLVideoElement.prototype.width;\\n\\n/** @type {number} */\\nHTMLVideoElement.prototype.height;\\n\\n/** @type {number} */\\nHTMLVideoElement.prototype.videoWidth;\\n\\n/** @type {number} */\\nHTMLVideoElement.prototype.videoHeight;\\n\\n/** @type {string} */\\nHTMLVideoElement.prototype.poster;\\n\\n/** @type {boolean} */\\nHTMLVideoElement.prototype.webkitSupportsFullscreen;\\n\\n/** @type {boolean} */\\nHTMLVideoElement.prototype.webkitDisplayingFullscreen;\\n\\n/** @type {number} */\\nHTMLVideoElement.prototype.webkitDecodedFrameCount;\\n\\n/** @type {number} */\\nHTMLVideoElement.prototype.webkitDroppedFrameCount;\\n\\n/**\\n * @typedef {{\\n * creationTime: number,\\n * totalVideoFrames: number,\\n * droppedVideoFrames: number,\\n * corruptedVideoFrames: number,\\n * totalFrameDelay: number\\n * }}\\n */\\nvar VideoPlaybackQuality;\\n\\n/**\\n * @see https://w3c.github.io/media-source/#htmlvideoelement-extensions\\n * @return {!VideoPlaybackQuality} Stats about the current playback.\\n */\\nHTMLVideoElement.prototype.getVideoPlaybackQuality = function() {};\\n\\n\\n/**\\n * @constructor\\n * @see https://html.spec.whatwg.org/multipage/media.html#error-codes\\n */\\nfunction MediaError() {}\\n\\n/** @type {number} */\\nMediaError.prototype.code;\\n\\n/** @type {string} */\\nMediaError.prototype.message;\\"; +a.a+="n\\n/**\\n * The fetching process for the media resource was aborted by the user agent at\\n * the user's request.\\n * @type {number}\\n */\\nMediaError.MEDIA_ERR_ABORTED;\\n\\n/**\\n * A network error of some description caused the user agent to stop fetching\\n * the media resource, after the resource was established to be usable.\\n * @type {number}\\n */\\nMediaError.MEDIA_ERR_NETWORK;\\n\\n/**\\n * An error of some description occurred while decoding the media resource,\\n * after the resource was established to be usable.\\n * @type {number}\\n */\\nMediaError.MEDIA_ERR_DECODE;\\n\\n/**\\n * The media resource indicated by the src attribute was not suitable.\\n * @type {number}\\n */\\nMediaError.MEDIA_ERR_SRC_NOT_SUPPORTED;\\n\\n// HTML5 MessageChannel\\n/**\\n * @see http://dev.w3.org/html5/spec/comms.html#messagechannel\\n * @constructor\\n */\\nfunction MessageChannel() {}\\n\\n/**\\n * Returns the first port.\\n * @type {!MessagePort}\\n */\\nMessageChannel.prototype.port1;\\n\\n/**\\n * Returns the second port.\\n * @type {!MessagePort}\\n */\\nMessageChannel.prototype.port2;\\n\\n// HTML5 MessagePort\\n/**\\n * @see http://dev.w3.org/html5/spec/comms.html#messageport\\n * @constructor\\n * @implements {EventTarget}\\n * @implements {Transferable}\\n */\\nfunction MessagePort() {}\\n\\n/** @override */\\nMessagePort.prototype.addEventListener = function(type, listener, opt_options) {\\n};\\n\\n/** @override */\\nMessagePort.prototype.removeEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nMessagePort.prototype.dispatchEvent = function(evt) {};\\n\\n\\n/**\\n * Posts a message through the channel, optionally with the given\\n * Array of Transferables.\\n * @param {*} message\\n * @param {Array=} opt_transfer\\n * @return {undefined}\\n */\\nMessagePort.prototype.postMessage = function(message, opt_transfer) {\\n};\\n\\n/**\\n * Begins dispatching messages received on the port.\\n * @return {undefined}\\n */\\nMessagePort.prototype.start = function() {};\\n\\n/**\\n * Disconnects the port, so that it is no longer active.\\n * @return {undefined}\\n */\\nMessagePort.prototype.close = function() {};\\n\\n/**\\n * TODO(blickly): Change this to MessageEvent<*> and add casts as needed\\n * @type {?function(!MessageEvent): void}\\n */\\nMessagePort.prototype.onmessage;\\n\\n// HTML5 MessageEvent class\\n/**\\n * @typedef {Window|MessagePort|ServiceWorker}\\n * @see https://html.spec.whatwg.org/multipage/comms.html#messageeventsource\\n */\\nvar MessageEventSource;\\n\\n\\n/**\\n * @record\\n * @extends {EventInit}\\n * @template T\\n * @see https://html.spec.whatwg.org/multipage/comms.html#messageeventinit\\n */\\nfunction MessageEventInit() {}\\n\\n/** @type {T|undefined} */\\nMessageEventInit.prototype.data;\\n\\n/** @type {(string|undefined)} */\\nMessageEventInit.prototype.origin;\\n\\n/** @type {(string|undefined)} */\\nMessageEventInit.prototype.lastEventId;\\n\\n/** @type {(?MessageEventSource|undefined)} */\\nMessageEventInit.prototype.source;\\n\\n/** @type {(!Array|undefined)} */\\nMessageEventInit.prototype.ports;\\n\\n\\n/**\\n * @see https://html.spec.whatwg.org/multipage/comms.html#messageevent\\n * @constructor\\n * @extends {Event}\\n * @param {string} type\\n * @param {MessageEventInit=} opt_eventInitDict\\n * @template T\\n */\\nfunction MessageEvent(type, opt_eventInitDict) {}\\n\\n/**\\n * The data payload of the message.\\n * @type {T}\\n */\\nMessageEvent.prototype.data;\\n\\n/**\\n * The origin of the message, for server-sent events and cross-document\\n * messaging.\\n * @type {string}\\n */\\nMessageEvent.prototype.origin;\\n\\n/**\\n * The last event ID, for server-sent events.\\n * @type {string}\\n */\\nMessageEvent.prototype.lastEventId;\\n\\n/**\\n * The window that dispatched the event.\\n * @type {Window}\\n */\\nMessageEvent.prototype.source;\\n\\n/**\\n * The Array of MessagePorts sent with the message, for cross-document\\n * messaging and channel messaging.\\n * @type {Array}\\n */\\nMessageEvent.prototype.ports;\\n\\n/**\\n * Initializes the event in a manner analogous to the similarly-named methods in\\n * the DOM Events interfaces.\\n * @param {string} typeArg\\n * @param {boolean=} canBubbleArg\\n * @param {boolean=} cancelableArg\\n * @param {T=} dataArg\\n * @param {string=} originArg\\n * @param {string=} lastEventIdArg\\n * @param {?MessageEventSource=} sourceArg\\n * @param {!Array=} portsArg\\n * @return {undefined}\\n */\\nMessageEvent.prototype.initMessageEvent = function(typeArg, canBubbleArg,\\n cancelableArg, dataArg, originArg, lastEventIdArg, sourceArg, portsArg) {};\\n\\n/**\\n * Initializes the event in a manner analogous to the similarly-named methods in\\n * the DOM Events interfaces.\\n * @param {string} namespaceURI\\n * @param {string=} typeArg\\n * @param {boolean=} canBubbleArg\\n * @param {boolean=} cancelableArg\\n * @param {T=} dataArg\\n * @param {string=} originArg\\n * @param {string=} lastEventIdArg\\n * @param {?MessageEventSource=} sourceArg\\n * @param {!Array=} portsArg\\n * @return {undefined}\\n */\\nMessageEvent.prototype.initMessageEventNS = function(namespaceURI, typeArg,\\n canBubbleArg, cancelableArg, dataArg, originArg, lastEventIdArg, sourceArg,\\n portsArg) {};\\n\\n\\n/**\\n * HTML5 BroadcastChannel class.\\n * @param {string} channelName\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel\\n * @see https://html.spec.whatwg.org/multipage/comms.html#dom-broadcastchannel\\n * @implements {EventTarget}\\n * @constructor\\n */\\nfunction BroadcastChannel(channelName) {}\\n\\n/**\\n * Sends the message, of any type of object, to each BroadcastChannel object\\n * listening to the same channel.\\n * @param {*} message\\n */\\nBroadcastChannel.prototype.postMessage;\\n\\n/**\\n * Closes the channel object, indicating it won't get any new messages, and\\n * allowing it to be, eventually, garbage collected.\\n * @return {void}\\n */\\nBroadcastChannel.prototype.close;\\n\\n/** @override */\\nBroadcastChannel.prototype.addEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nBroadcastChannel.prototype.dispatchEvent = function(evt) {};\\n\\n/** @override */\\nBroadcastChannel.prototype.removeEventListener = function(\\n type, listener, opt_options) {};\\n\\n/**\\n * An EventHandler property that specifies the function to execute when a\\n * message event is fired on this object.\\n * @type {?function(!MessageEvent<*>)}\\n */\\nBroadcastChannel.prototype.onmessage;\\n\\n/**\\n * The name of the channel.\\n * @type {string}\\n */\\nBroadcastChannel.prototype.name;\\n\\n/**\\n * HTML5 DataTransfer class.\\n *\\n * We say that this extends ClipboardData, because Event.prototype.clipboardData\\n * is a DataTransfer on WebKit but a ClipboardData on IE. The interfaces are so\\n * similar that it's easier to merge them.\\n *\\n * @see http://www.w3.org/TR/2011/WD-html5-20110113/dnd.html\\n * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html\\n * @see http://developers.whatwg.org/dnd.html#datatransferitem\\n * @constructor\\n * @extends {ClipboardData}\\n */\\nfunction DataTransfer() {}\\n\\n/** @type {string} */\\nDataTransfer.prototype.dropEffect;\\n\\n/** @type {string} */\\nDataTransfer.prototype.effectAllowed;\\n\\n/** @type {!Array} */\\nDataTransfer.prototype.types;\\n\\n/** @type {!FileList} */\\nDataTransfer.prototype.files;\\n\\n/**\\n * @param {string=} opt_format Format for which to remove data.\\n * @override\\n * @return {undefined}\\n */\\nDataTransfer.prototype.clearData = function(opt_format) {};\\n\\n/**\\n * @param {string} format Format for which to set data.\\n * @param {string} data Data to add.\\n * @override\\n * @return {boolean}\\n */\\nDataTransfer.prototype.setData = function(format, data) {};\\n\\n/**\\n * @param {string} format Format for which to set data.\\n * @return {string} Data for the given format.\\n * @override\\n */\\nDataTransfer.prototype.getData = function(format) { return ''; };\\n\\n/**\\n * @param {HTMLElement} img The image to use when dragging.\\n * @param {number} x Horizontal position of the cursor.\\n * @param {number} y Vertical position of the cursor.\\n * @return {undefined}\\n */\\nDataTransfer.prototype.setDragImage = function(img, x, y) {};\\n\\n/**\\n * @param {HTMLElement} elem Element to receive drag result events.\\n * @return {undefined}\\n */\\nDataTransfer.prototype.addElement = function(elem) {};\\n\\n/**\\n * Addition for accessing clipboard file data that are part of the proposed\\n * HTML5 spec.\\n * @type {DataTransfer}\\n */\\nMouseEvent.prototype.dataTransfer;\\n\\n/**\\n * @record\\n * @extends {MouseEventInit}\\n * @see https://w3c.github.io/uievents/#idl-wheeleventinit\\n */\\nfunction WheelEventInit() {}\\n\\n/** @type {undefined|number} */\\nWheelEventInit.prototype.deltaX;\\n\\n/** @type {undefined|number} */\\nWheelEventInit.prototype.deltaY;\\n\\n/** @type {undefined|number} */\\nWheelEventInit.prototype.deltaZ;\\n\\n/** @type {undefined|number} */\\nWheelEventInit.prototype.deltaMode;\\n\\n/**\\n * @param {string} type\\n * @param {WheelEventInit=} opt_eventInitDict\\n * @see http://www.w3.org/TR/DOM-Level-3-Events/#interface-WheelEvent\\n * @constructor\\n * @extends {MouseEvent}\\n */\\nfunction WheelEvent(type, opt_eventInitDict) {}\\n\\n/** @type {number} */\\nWheelEvent.DOM_DELTA_PIXEL;\\n\\n/** @type {number} */\\nWheelEvent.DOM_DELTA_LINE;\\n\\n/** @type {number} */\\nWheelEvent.DOM_DELTA_PAGE;\\n\\n/** @const {number} */\\nWheelEvent.prototype.deltaX;\\n\\n/** @const {number} */\\nWheelEvent.prototype.deltaY;\\n\\n/** @const {number} */\\nWheelEvent.prototype.deltaZ;\\n\\n/** @const {number} */\\nWheelEvent.prototype.deltaMode;\\n\\n/**\\n * HTML5 DataTransferItem class.\\n *\\n * @see http://www.w3.org/TR/2011/WD-html5-20110113/dnd.html\\n * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html\\n * @see http://developers.whatwg.org/dnd.html#datatransferitem\\n * @constructor\\n */\\nfunction DataTransferItem() {}\\n\\n/** @type {string} */\\nDataTransferItem.prototype.kind;\\n\\n/** @type {string} */\\nDataTransferItem.prototype.type;\\n\\n/**\\n * @param {function(string)} callback\\n * @return {undefined}\\n */\\nDataTransferItem.prototype.getAsString = function(callback) {};\\n\\n/**\\n * @return {?File} The file corresponding to this item, or null.\\n * @nosideeffects\\n */\\nDataTransferItem.prototype.getAsFile = function() { return null; };\\n\\n/**\\n * @return {?Entry} The Entry corresponding to this item, or null. Note that\\n * despite its name,this method only works in Chrome, and will eventually\\n * be renamed to {@code getAsEntry}.\\n * @nosideeffects\\n */\\nDataTransferItem.prototype.webkitGetAsEntry = function() { return null; };\\n\\n/**\\n * HTML5 DataTransferItemList class. There are some discrepancies in the docs\\n * on the whatwg.org site. When in doubt, these prototypes match what is\\n * implemented as of Chrome 30.\\n *\\n * @see http://www.w3.org/TR/2011/WD-html5-20110113/dnd.html\\n * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html\\n * @see http://developers.whatwg.org/dnd.html#datatransferitem\\n * @constructor\\n * @implements {IArrayLike}\\n */\\nfunction DataTransferItemList() {}\\n\\n/** @type {number} */\\nDataTransferItemList.prototype.length;\\n\\n/**\\n * @param {number} i File to return from the list.\\n * @return {DataTransferItem} The ith DataTransferItem in the list, or null.\\n * @nosideeffects\\n */\\nDataTransferItemList.prototype.item = function(i) { return null; };\\n\\n/**\\n * Adds an item to the list.\\n * @param {string|!File} data Data for the item being added.\\n * @param {string=} opt_type Mime type of the item being added. MUST be present\\n * if the {@code data} parameter is a string.\\n * @return {DataTransferItem}\\n */\\nDataTransferItemList.prototype.add = function(data, opt_type) {};\\n\\n/**\\n * Removes an item from the list.\\n * @param {number} i File to remove from the list.\\n * @return {undefined}\\n */\\nDataTransferItemList.prototype.remove = function(i) {};\\n\\n/**\\n * Removes all items from the list.\\n * @return {undefined}\\n */\\nDataTransferItemList.prototype.clear = function() {};\\n\\n/** @type {!DataTransferItemList} */\\nDataTransfer.prototype.items;\\n\\n/**\\n * @record\\n * @extends {MouseEventInit}\\n * @see http://w3c.github.io/html/editing.html#dictdef-drageventinit\\n */\\nfunction DragEventInit() {}\\n\\n/** @type {undefined|?DataTransfer} */\\nDragEventInit.prototype.dataTransfer;\\n\\n\\n/**\\n * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#the-dragevent-interface\\n * @constructor\\n * @extends {MouseEvent}\\n * @param {string} type\\n * @param {DragEventInit=} opt_eventInitDict\\n */\\nfunction DragEvent(type, opt_eventInitDict) {}\\n\\n/** @type {DataTransfer} */\\nDragEvent.prototype.dataTransfer;\\n\\n\\n/**\\n * @record\\n * @extends {EventInit}\\n * @see https://www.w3.org/TR/progress-events/#progresseventinit\\n */\\nfunction ProgressEventInit() {}\\n\\n/** @type {undefined|boolean} */\\nProgressEventInit.prototype.lengthComputable;\\n\\n/** @type {undefined|number} */\\nProgressEventInit.prototype.loaded;\\n\\n/** @type {undefined|number} */\\nProgressEventInit.prototype.total;\\n\\n/**\\n * @constructor\\n * @param {string} type\\n * @param {ProgressEventInit=} opt_progressEventInitDict\\n * @extends {Event}\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/ProgressEvent\\n */\\nfunction ProgressEvent(type, opt_progressEventInitDict) {}\\n\\n/** @type {number} */\\nProgressEvent.prototype.total;\\n\\n/** @type {number} */\\nProgressEvent.prototype.loaded;\\n\\n/** @type {boolean} */\\nProgressEvent.prototype.lengthComputable;\\n\\n\\n/**\\n * @constructor\\n */\\nfunction TimeRanges() {}\\n\\n/** @type {number} */\\nTimeRanges.prototype.length;\\n\\n/**\\n * @param {number} index The index.\\n * @return {number} The start time of the range at index.\\n * @throws {DOMException}\\n */\\nTimeRanges.prototype.start = function(index) { return 0; };\\n\\n/**\\n * @param {number} index The index.\\n * @return {number} The end time of the range at index.\\n * @throws {DOMException}\\n */\\nTimeRanges.prototype.end = function(index) { return 0; };\\n\\n\\n// HTML5 Web Socket class\\n/**\\n * @see http://dev.w3.org/html5/websockets/\\n * @constructor\\n * @param {string} url\\n * @param {string=} opt_protocol\\n * @implements {EventTarget}\\n */\\nfunction WebSocket(url, opt_protocol) {}\\n\\n/**\\n * The connection has not yet been established.\\n * @type {number}\\n */\\nWebSocket.CONNECTING = 0;\\n\\n/**\\n * The WebSocket connection is established and communication is possible.\\n * @type {number}\\n */\\nWebSocket.OPEN = 1;\\n\\n/**\\n * The connection is going through the closing handshake, or the close() method has been invoked.\\n * @type {number}\\n */\\nWebSocket.CLOSING = 2;\\n\\n/**\\n * The connection has been closed or could not be opened.\\n * @type {number}\\n */\\nWebSocket.CLOSED = 3;\\n\\n/** @override */\\nWebSocket.prototype.addEventListener = function(type, listener, opt_options) {};\\n\\n/** @override */\\nWebSocket.prototype.removeEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nWebSocket.prototype.dispatchEvent = function(evt) {};\\n\\n/**\\n * Returns the URL value that was passed to the constructor.\\n * @type {string}\\n */\\nWebSocket.prototype.url;\\n\\n/**\\n * Represents the state of the connection.\\n * @type {number}\\n */\\nWebSocket.prototype.readyState;\\n\\n/**\\n * Returns the number of bytes that have been queued but not yet sent.\\n * @type {number}\\n */\\nWebSocket.prototype.bufferedAmount;\\n\\n/**\\n * An event handler called on open event.\\n * @type {?function(!Event)}\\n */\\nWebSocket.prototype.onopen;\\n\\n/**\\n * An event handler called on message event.\\n * TODO(blickly): Change this to MessageEvent<*> and add casts as needed\\n * @type {?function(!MessageEvent): void}\\n */\\nWebSocket.prototype.onmessage;\\n\\n/**\\n * An event handler called on close event.\\n * @type {?function(!Event)}\\n */\\nWebSocket.prototype.onclose;\\n\\n/**\\n * Transmits data using the connection.\\n * @param {string|ArrayBuffer|ArrayBufferView} data\\n * @return {boolean}\\n */\\nWebSocket.prototype.send = function(data) {};\\n\\n/**\\n * Closes the Web Socket connection or connection attempt, if any.\\n * @param {number=} opt_code\\n * @param {string=} opt_reason\\n * @return {undefined}\\n */\\nWebSocket.prototype.close = function(opt_code, opt_reason) {};\\n\\n/**\\n * @type {string} Sets the type of data (blob or arraybuffer) for binary data.\\n */\\nWebSocket.prototype.binaryType;\\n\\n// HTML5 History\\n/**\\n * @constructor\\n */\\nfunction History() {}\\"; +a.a+="n\\n/**\\n * Pushes a new state into the session history.\\n * @see http://www.w3.org/TR/html5/history.html#the-history-interface\\n * @param {*} data New state.\\n * @param {string} title The title for a new session history entry.\\n * @param {string=} opt_url The URL for a new session history entry.\\n * @return {undefined}\\n */\\nHistory.prototype.pushState = function(data, title, opt_url) {};\\n\\n/**\\n * Replaces the current state in the session history.\\n * @see http://www.w3.org/TR/html5/history.html#the-history-interface\\n * @param {*} data New state.\\n * @param {string} title The title for a session history entry.\\n * @param {string=} opt_url The URL for a new session history entry.\\n * @return {undefined}\\n */\\nHistory.prototype.replaceState = function(data, title, opt_url) {};\\n\\n/**\\n * Pending state object.\\n * @see https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history#Reading_the_current_state\\n * @type {*}\\n */\\nHistory.prototype.state;\\n\\n/**\\n * Allows web applications to explicitly set default scroll restoration behavior\\n * on history navigation. This property can be either auto or manual.\\n *\\n * Non-standard. Only supported in Chrome 46+.\\n *\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/History\\n * @see https://majido.github.io/scroll-restoration-proposal/history-based-api.html\\n * @type {string}\\n */\\nHistory.prototype.scrollRestoration;\\n\\n/**\\n * Add history property to Window.\\n *\\n * @type {!History}\\n */\\nWindow.prototype.history;\\n\\n/**\\n * @see http://www.whatwg.org/specs/web-apps/current-work/#popstateevent\\n * @constructor\\n * @extends {Event}\\n *\\n * @param {string} type\\n * @param {{state: *}=} opt_eventInitDict\\n */\\nfunction PopStateEvent(type, opt_eventInitDict) {}\\n\\n/**\\n * @type {*}\\n */\\nPopStateEvent.prototype.state;\\n\\n/**\\n * Initializes the event after it has been created with document.createEvent\\n * @param {string} typeArg\\n * @param {boolean} canBubbleArg\\n * @param {boolean} cancelableArg\\n * @param {*} stateArg\\n * @return {undefined}\\n */\\nPopStateEvent.prototype.initPopStateEvent = function(typeArg, canBubbleArg,\\n cancelableArg, stateArg) {};\\n\\n/**\\n * @see http://www.whatwg.org/specs/web-apps/current-work/#hashchangeevent\\n * @constructor\\n * @extends {Event}\\n *\\n * @param {string} type\\n * @param {{oldURL: string, newURL: string}=} opt_eventInitDict\\n */\\nfunction HashChangeEvent(type, opt_eventInitDict) {}\\n\\n/** @type {string} */\\nHashChangeEvent.prototype.oldURL;\\n\\n/** @type {string} */\\nHashChangeEvent.prototype.newURL;\\n\\n/**\\n * Initializes the event after it has been created with document.createEvent\\n * @param {string} typeArg\\n * @param {boolean} canBubbleArg\\n * @param {boolean} cancelableArg\\n * @param {string} oldURLArg\\n * @param {string} newURLArg\\n * @return {undefined}\\n */\\nHashChangeEvent.prototype.initHashChangeEvent = function(typeArg, canBubbleArg,\\n cancelableArg, oldURLArg, newURLArg) {};\\n\\n/**\\n * @see http://www.whatwg.org/specs/web-apps/current-work/#pagetransitionevent\\n * @constructor\\n * @extends {Event}\\n *\\n * @param {string} type\\n * @param {{persisted: boolean}=} opt_eventInitDict\\n */\\nfunction PageTransitionEvent(type, opt_eventInitDict) {}\\n\\n/** @type {boolean} */\\nPageTransitionEvent.prototype.persisted;\\n\\n/**\\n * Initializes the event after it has been created with document.createEvent\\n * @param {string} typeArg\\n * @param {boolean} canBubbleArg\\n * @param {boolean} cancelableArg\\n * @param {*} persistedArg\\n * @return {undefined}\\n */\\nPageTransitionEvent.prototype.initPageTransitionEvent = function(typeArg,\\n canBubbleArg, cancelableArg, persistedArg) {};\\n\\n/**\\n * @constructor\\n * @implements {IArrayLike}\\n */\\nfunction FileList() {}\\n\\n/** @type {number} */\\nFileList.prototype.length;\\n\\n/**\\n * @param {number} i File to return from the list.\\n * @return {File} The ith file in the list.\\n * @nosideeffects\\n */\\nFileList.prototype.item = function(i) { return null; };\\n\\n/**\\n * @type {boolean}\\n * @see http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#withcredentials\\n */\\nXMLHttpRequest.prototype.withCredentials;\\n\\n/**\\n * @type {?function(!ProgressEvent): void}\\n * @see https://xhr.spec.whatwg.org/#handler-xhr-onloadstart\\n */\\nXMLHttpRequest.prototype.onloadstart;\\n\\n/**\\n * @type {?function(!ProgressEvent): void}\\n * @see https://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#handler-xhr-onprogress\\n */\\nXMLHttpRequest.prototype.onprogress;\\n\\n/**\\n * @type {?function(!ProgressEvent): void}\\n * @see https://xhr.spec.whatwg.org/#handler-xhr-onabort\\n */\\nXMLHttpRequest.prototype.onabort;\\n\\n/**\\n * @type {?function(!ProgressEvent): void}\\n * @see https://xhr.spec.whatwg.org/#handler-xhr-onload\\n */\\nXMLHttpRequest.prototype.onload;\\n\\n/**\\n * @type {?function(!ProgressEvent): void}\\n * @see https://xhr.spec.whatwg.org/#handler-xhr-ontimeout\\n */\\nXMLHttpRequest.prototype.ontimeout;\\n\\n/**\\n * @type {?function(!ProgressEvent): void}\\n * @see https://xhr.spec.whatwg.org/#handler-xhr-onloadend\\n */\\nXMLHttpRequest.prototype.onloadend;\\n\\n/**\\n * @type {XMLHttpRequestUpload}\\n * @see http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#the-upload-attribute\\n */\\nXMLHttpRequest.prototype.upload;\\n\\n/**\\n * @param {string} mimeType The mime type to override with.\\n * @return {undefined}\\n */\\nXMLHttpRequest.prototype.overrideMimeType = function(mimeType) {};\\n\\n/**\\n * @type {string}\\n * @see http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#the-responsetype-attribute\\n */\\nXMLHttpRequest.prototype.responseType;\\n\\n/**\\n * @type {?(ArrayBuffer|Blob|Document|Object|string)}\\n * @see http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#the-response-attribute\\n */\\nXMLHttpRequest.prototype.response;\\n\\n\\n/**\\n * @type {ArrayBuffer}\\n * Implemented as a draft spec in Firefox 4 as the way to get a requested array\\n * buffer from an XMLHttpRequest.\\n * @see https://developer.mozilla.org/En/Using_XMLHttpRequest#Receiving_binary_data_using_JavaScript_typed_arrays\\n *\\n * This property is not used anymore and should be removed.\\n * @see https://github.com/google/closure-compiler/pull/1389\\n */\\nXMLHttpRequest.prototype.mozResponseArrayBuffer;\\n\\n/**\\n * XMLHttpRequestEventTarget defines events for checking the status of a data\\n * transfer between a client and a server. This should be a common base class\\n * for XMLHttpRequest and XMLHttpRequestUpload.\\n *\\n * @constructor\\n * @implements {EventTarget}\\n */\\nfunction XMLHttpRequestEventTarget() {}\\n\\n/** @override */\\nXMLHttpRequestEventTarget.prototype.addEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nXMLHttpRequestEventTarget.prototype.removeEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nXMLHttpRequestEventTarget.prototype.dispatchEvent = function(evt) {};\\n\\n/**\\n * An event target to track the status of an upload.\\n *\\n * @constructor\\n * @extends {XMLHttpRequestEventTarget}\\n */\\nfunction XMLHttpRequestUpload() {}\\n\\n/**\\n * @type {?function(!ProgressEvent): void}\\n * @see https://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#handler-xhr-onprogress\\n */\\nXMLHttpRequestUpload.prototype.onprogress;\\n\\n/**\\n * @param {number=} opt_width\\n * @param {number=} opt_height\\n * @constructor\\n * @extends {HTMLImageElement}\\n */\\nfunction Image(opt_width, opt_height) {}\\n\\n\\n/**\\n * Dataset collection.\\n * This is really a DOMStringMap but it behaves close enough to an object to\\n * pass as an object.\\n * @type {!Object}\\n * @const\\n */\\nHTMLElement.prototype.dataset;\\n\\n\\n/**\\n * @constructor\\n * @implements {IArrayLike}\\n * @see https://dom.spec.whatwg.org/#interface-domtokenlist\\n */\\nfunction DOMTokenList() {}\\n\\n/**\\n * Returns the number of CSS classes applied to this Element.\\n * @type {number}\\n */\\nDOMTokenList.prototype.length;\\n\\n/**\\n * @param {number} index The index of the item to return.\\n * @return {string} The CSS class at the specified index.\\n * @nosideeffects\\n */\\nDOMTokenList.prototype.item = function(index) {};\\n\\n/**\\n * @param {string} token The CSS class to check for.\\n * @return {boolean} Whether the CSS class has been applied to the Element.\\n * @nosideeffects\\n */\\nDOMTokenList.prototype.contains = function(token) {};\\n\\n/**\\n * @param {...string} var_args The CSS class(es) to add to this element.\\n * @return {undefined}\\n */\\nDOMTokenList.prototype.add = function(var_args) {};\\n\\n/**\\n * @param {...string} var_args The CSS class(es) to remove from this element.\\n * @return {undefined}\\n */\\nDOMTokenList.prototype.remove = function(var_args) {};\\n\\n/**\\n * Replaces token with newToken.\\n * @param {string} token The CSS class to replace.\\n * @param {string} newToken The new CSS class to use.\\n * @return {undefined}\\n */\\nDOMTokenList.prototype.replace = function(token, newToken) {};\\n\\n/**\\n * @param {string} token The CSS class to toggle from this element.\\n * @param {boolean=} opt_force True to add the class whether it exists\\n * or not. False to remove the class whether it exists or not.\\n * This argument is not supported on IE 10 and below, according to\\n * the MDN page linked below.\\n * @return {boolean} False if the token was removed; True otherwise.\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Element.classList\\n */\\nDOMTokenList.prototype.toggle = function(token, opt_force) {};\\n\\n/**\\n * @return {string} A stringified representation of CSS classes.\\n * @nosideeffects\\n * @override\\n */\\nDOMTokenList.prototype.toString = function() {};\\n\\n/**\\n * @return {!IteratorIterable} An iterator to go through all values of\\n * the key/value pairs contained in this object.\\n * @nosideeffects\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList/values\\n */\\nDOMTokenList.prototype.values = function() {};\\n\\n/**\\n * A better interface to CSS classes than className.\\n * @type {!DOMTokenList}\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/classList\\n * @const\\n */\\nElement.prototype.classList;\\n\\n/**\\n * Constraint Validation API properties and methods\\n * @see http://www.w3.org/TR/2009/WD-html5-20090423/forms.html#the-constraint-validation-api\\n */\\n\\n/** @return {boolean} */\\nHTMLFormElement.prototype.checkValidity = function() {};\\n\\n/** @return {boolean} */\\nHTMLFormElement.prototype.reportValidity = function() {};\\n\\n/** @type {boolean} */\\nHTMLFormElement.prototype.noValidate;\\n\\n/** @constructor */\\nfunction ValidityState() {}\\n\\n/** @type {boolean} */\\nValidityState.prototype.badInput;\\n\\n/** @type {boolean} */\\nValidityState.prototype.customError;\\n\\n/** @type {boolean} */\\nValidityState.prototype.patternMismatch;\\n\\n/** @type {boolean} */\\nValidityState.prototype.rangeOverflow;\\n\\n/** @type {boolean} */\\nValidityState.prototype.rangeUnderflow;\\n\\n/** @type {boolean} */\\nValidityState.prototype.stepMismatch;\\n\\n/** @type {boolean} */\\nValidityState.prototype.typeMismatch;\\n\\n/** @type {boolean} */\\nValidityState.prototype.tooLong;\\n\\n/** @type {boolean} */\\nValidityState.prototype.tooShort;\\n\\n/** @type {boolean} */\\nValidityState.prototype.valid;\\n\\n/** @type {boolean} */\\nValidityState.prototype.valueMissing;\\n\\n\\n/** @type {boolean} */\\nHTMLButtonElement.prototype.autofocus;\\n\\n/**\\n * Can return null when hidden.\\n * See https://html.spec.whatwg.org/multipage/forms.html#dom-lfe-labels\\n * @const\\n * @type {?NodeList}\\n */\\nHTMLButtonElement.prototype.labels;\\n\\n/** @type {string} */\\nHTMLButtonElement.prototype.validationMessage;\\n\\n/**\\n * @const\\n * @type {ValidityState}\\n */\\nHTMLButtonElement.prototype.validity;\\n\\n/** @type {boolean} */\\nHTMLButtonElement.prototype.willValidate;\\n\\n/** @return {boolean} */\\nHTMLButtonElement.prototype.checkValidity = function() {};\\n\\n/** @return {boolean} */\\nHTMLButtonElement.prototype.reportValidity = function() {};\\n\\n/**\\n * @param {string} message\\n * @return {undefined}\\n */\\nHTMLButtonElement.prototype.setCustomValidity = function(message) {};\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/html5/forms.html#attr-fs-formaction\\n */\\nHTMLButtonElement.prototype.formAction;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/html5/forms.html#attr-fs-formenctype\\n */\\nHTMLButtonElement.prototype.formEnctype;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/html5/forms.html#attr-fs-formmethod\\n */\\nHTMLButtonElement.prototype.formMethod;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/html5/forms.html#attr-fs-formtarget\\n */\\nHTMLButtonElement.prototype.formTarget;\\n\\n/** @type {boolean} */\\nHTMLInputElement.prototype.autofocus;\\n\\n/** @type {boolean} */\\nHTMLInputElement.prototype.formNoValidate;\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n * @see http://www.w3.org/TR/html5/forms.html#attr-fs-formaction\\n */\\nHTMLInputElement.prototype.formAction;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/html5/forms.html#attr-fs-formenctype\\n */\\nHTMLInputElement.prototype.formEnctype;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/html5/forms.html#attr-fs-formmethod\\n */\\nHTMLInputElement.prototype.formMethod;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/html5/forms.html#attr-fs-formtarget\\n */\\nHTMLInputElement.prototype.formTarget;\\n\\n/**\\n * Can return null when hidden.\\n * See https://html.spec.whatwg.org/multipage/forms.html#dom-lfe-labels\\n * @const\\n * @type {?NodeList}\\n */\\nHTMLInputElement.prototype.labels;\\n\\n/** @type {string} */\\nHTMLInputElement.prototype.validationMessage;\\n\\n/**\\n * @const\\n * @type {ValidityState}\\n */\\nHTMLInputElement.prototype.validity;\\n\\n/** @type {boolean} */\\nHTMLInputElement.prototype.willValidate;\\n\\n/** @return {boolean} */\\nHTMLInputElement.prototype.checkValidity = function() {};\\n\\n/** @return {boolean} */\\nHTMLInputElement.prototype.reportValidity = function() {};\\n\\n/**\\n * @param {string} message\\n * @return {undefined}\\n */\\nHTMLInputElement.prototype.setCustomValidity = function(message) {};\\n\\n/** @type {Element} */\\nHTMLLabelElement.prototype.control;\\n\\n/** @type {boolean} */\\nHTMLSelectElement.prototype.autofocus;\\n\\n/**\\n * Can return null when hidden.\\n * See https://html.spec.whatwg.org/multipage/forms.html#dom-lfe-labels\\n * @const\\n * @type {?NodeList}\\n */\\nHTMLSelectElement.prototype.labels;\\n\\n/** @type {HTMLCollection} */\\nHTMLSelectElement.prototype.selectedOptions;\\n\\n/** @type {string} */\\nHTMLSelectElement.prototype.validationMessage;\\n\\n/**\\n * @const\\n * @type {ValidityState}\\n */\\nHTMLSelectElement.prototype.validity;\\n\\n/** @type {boolean} */\\nHTMLSelectElement.prototype.willValidate;\\n\\n/** @return {boolean} */\\nHTMLSelectElement.prototype.checkValidity = function() {};\\n\\n/** @return {boolean} */\\nHTMLSelectElement.prototype.reportValidity = function() {};\\n\\n/**\\n * @param {string} message\\n * @return {undefined}\\n */\\nHTMLSelectElement.prototype.setCustomValidity = function(message) {};\\n\\n/** @type {boolean} */\\nHTMLTextAreaElement.prototype.autofocus;\\n\\n/**\\n * Can return null when hidden.\\n * See https://html.spec.whatwg.org/multipage/forms.html#dom-lfe-labels\\n * @const\\n * @type {?NodeList}\\n */\\nHTMLTextAreaElement.prototype.labels;\\n\\n/** @type {string} */\\nHTMLTextAreaElement.prototype.placeholder;\\n\\n/** @type {string} */\\nHTMLTextAreaElement.prototype.validationMessage;\\n\\n/**\\n * @const\\n * @type {ValidityState}\\n */\\nHTMLTextAreaElement.prototype.validity;\\n\\n/** @type {boolean} */\\nHTMLTextAreaElement.prototype.willValidate;\\n\\n/** @return {boolean} */\\nHTMLTextAreaElement.prototype.checkValidity = function() {};\\n\\n/** @return {boolean} */\\nHTMLTextAreaElement.prototype.reportValidity = function() {};\\n\\n/**\\n * @param {string} message\\n * @return {undefined}\\n */\\nHTMLTextAreaElement.prototype.setCustomValidity = function(message) {};\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n * @see http://www.w3.org/TR/html5/the-embed-element.html#htmlembedelement\\n */\\nfunction HTMLEmbedElement() {}\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/html5/dimension-attributes.html#dom-dim-width\\n */\\nHTMLEmbedElement.prototype.width;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/html5/dimension-attributes.html#dom-dim-height\\n */\\nHTMLEmbedElement.prototype.height;\\n\\n/**\\n * @type {str"; +a.a+="ing}\\n * @implicitCast\\n * @see http://www.w3.org/TR/html5/the-embed-element.html#dom-embed-src\\n */\\nHTMLEmbedElement.prototype.src;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/html5/the-embed-element.html#dom-embed-type\\n */\\nHTMLEmbedElement.prototype.type;\\n\\n// Fullscreen APIs.\\n\\n/**\\n * @record\\n * @see https://fullscreen.spec.whatwg.org/#dictdef-fullscreenoptions\\n */\\nfunction FullscreenOptions() {}\\n\\n/** @type {string} */\\nFullscreenOptions.prototype.navigationUI;\\n\\n/**\\n * @see https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen\\n * @param {!FullscreenOptions=} options\\n * @return {undefined}\\n */\\nElement.prototype.requestFullscreen = function(options) {};\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/2012/WD-fullscreen-20120703/#dom-document-fullscreenenabled\\n */\\nDocument.prototype.fullscreenEnabled;\\n\\n/**\\n * @type {Element}\\n * @see http://www.w3.org/TR/2012/WD-fullscreen-20120703/#dom-document-fullscreenelement\\n */\\nDocument.prototype.fullscreenElement;\\n\\n/**\\n * @see http://www.w3.org/TR/2012/WD-fullscreen-20120703/#dom-document-exitfullscreen\\n * @return {undefined}\\n */\\nDocument.prototype.exitFullscreen = function() {};\\n\\n// Externs definitions of browser current implementations.\\n// Firefox 10 implementation.\\nElement.prototype.mozRequestFullScreen = function() {};\\n\\nElement.prototype.mozRequestFullScreenWithKeys = function() {};\\n\\n/** @type {boolean} */\\nDocument.prototype.mozFullScreen;\\n\\nDocument.prototype.mozCancelFullScreen = function() {};\\n\\n/** @type {Element} */\\nDocument.prototype.mozFullScreenElement;\\n\\n/** @type {boolean} */\\nDocument.prototype.mozFullScreenEnabled;\\n\\n// Chrome 21 implementation.\\n/**\\n * The current fullscreen element for the document is set to this element.\\n * Valid only for Webkit browsers.\\n * @param {number=} opt_allowKeyboardInput Whether keyboard input is desired.\\n * Should use ALLOW_KEYBOARD_INPUT constant.\\n * @return {undefined}\\n */\\nElement.prototype.webkitRequestFullScreen = function(opt_allowKeyboardInput) {};\\n\\n/**\\n * The current fullscreen element for the document is set to this element.\\n * Valid only for Webkit browsers.\\n * @param {number=} opt_allowKeyboardInput Whether keyboard input is desired.\\n * Should use ALLOW_KEYBOARD_INPUT constant.\\n * @return {undefined}\\n */\\nElement.prototype.webkitRequestFullscreen = function(opt_allowKeyboardInput) {};\\n\\n/** @type {boolean} */\\nDocument.prototype.webkitIsFullScreen;\\n\\nDocument.prototype.webkitCancelFullScreen = function() {};\\n\\n/** @type {boolean} */\\nDocument.prototype.webkitFullscreenEnabled;\\n\\n/** @type {Element} */\\nDocument.prototype.webkitCurrentFullScreenElement;\\n\\n/** @type {Element} */\\nDocument.prototype.webkitFullscreenElement;\\n\\n/** @type {boolean} */\\nDocument.prototype.webkitFullScreenKeyboardInputAllowed;\\n\\n// IE 11 implementation.\\n// http://msdn.microsoft.com/en-us/library/ie/dn265028(v=vs.85).aspx\\n/** @return {void} */\\nElement.prototype.msRequestFullscreen = function() {};\\n\\n/** @return {void} */\\nDocument.prototype.msExitFullscreen = function() {};\\n\\n/** @type {boolean} */\\nDocument.prototype.msFullscreenEnabled;\\n\\n/** @type {Element} */\\nDocument.prototype.msFullscreenElement;\\n\\n/** @type {number} */\\nElement.ALLOW_KEYBOARD_INPUT = 1;\\n\\n/** @type {number} */\\nElement.prototype.ALLOW_KEYBOARD_INPUT = 1;\\n\\n\\n/**\\n * @typedef {{\\n * childList: (boolean|undefined),\\n * attributes: (boolean|undefined),\\n * characterData: (boolean|undefined),\\n * subtree: (boolean|undefined),\\n * attributeOldValue: (boolean|undefined),\\n * characterDataOldValue: (boolean|undefined),\\n * attributeFilter: (!Array|undefined)\\n * }}\\n */\\nvar MutationObserverInit;\\n\\n\\n/** @constructor */\\nfunction MutationRecord() {}\\n\\n/** @type {string} */\\nMutationRecord.prototype.type;\\n\\n/** @type {Node} */\\nMutationRecord.prototype.target;\\n\\n/** @type {!NodeList} */\\nMutationRecord.prototype.addedNodes;\\n\\n/** @type {!NodeList} */\\nMutationRecord.prototype.removedNodes;\\n\\n/** @type {?Node} */\\nMutationRecord.prototype.previousSibling;\\n\\n/** @type {?Node} */\\nMutationRecord.prototype.nextSibling;\\n\\n/** @type {?string} */\\nMutationRecord.prototype.attributeName;\\n\\n/** @type {?string} */\\nMutationRecord.prototype.attributeNamespace;\\n\\n/** @type {?string} */\\nMutationRecord.prototype.oldValue;\\n\\n\\n/**\\n * @see http://www.w3.org/TR/domcore/#mutation-observers\\n * @param {function(!Array, !MutationObserver)} callback\\n * @constructor\\n */\\nfunction MutationObserver(callback) {}\\n\\n/**\\n * @param {Node} target\\n * @param {MutationObserverInit=} options\\n * @return {undefined}\\n */\\nMutationObserver.prototype.observe = function(target, options) {};\\n\\nMutationObserver.prototype.disconnect = function() {};\\n\\n/**\\n * @return {!Array}\\n */\\nMutationObserver.prototype.takeRecords = function() {};\\n\\n/**\\n * @type {function(new:MutationObserver, function(Array))}\\n */\\nWindow.prototype.WebKitMutationObserver;\\n\\n/**\\n * @type {function(new:MutationObserver, function(Array))}\\n */\\nWindow.prototype.MozMutationObserver;\\n\\n\\n/**\\n * @see http://www.w3.org/TR/page-visibility/\\n * @type {VisibilityState}\\n */\\nDocument.prototype.visibilityState;\\n\\n/**\\n * @type {string}\\n */\\nDocument.prototype.mozVisibilityState;\\n\\n/**\\n * @type {string}\\n */\\nDocument.prototype.webkitVisibilityState;\\n\\n/**\\n * @type {string}\\n */\\nDocument.prototype.msVisibilityState;\\n\\n/**\\n * @see http://www.w3.org/TR/page-visibility/\\n * @type {boolean}\\n */\\nDocument.prototype.hidden;\\n\\n/**\\n * @type {boolean}\\n */\\nDocument.prototype.mozHidden;\\n\\n/**\\n * @type {boolean}\\n */\\nDocument.prototype.webkitHidden;\\n\\n/**\\n * @type {boolean}\\n */\\nDocument.prototype.msHidden;\\n\\n/**\\n * @see http://www.w3.org/TR/components-intro/\\n * @see http://w3c.github.io/webcomponents/spec/custom/#extensions-to-document-interface-to-register\\n * @param {string} type\\n * @param {{extends: (string|undefined), prototype: (Object|undefined)}=} options\\n * @return {!function(new:Element, ...*)} a constructor for the new tag.\\n * @deprecated document.registerElement() is deprecated in favor of customElements.define()\\n */\\nDocument.prototype.registerElement = function(type, options) {};\\n\\n/**\\n * @see http://www.w3.org/TR/components-intro/\\n * @see http://w3c.github.io/webcomponents/spec/custom/#extensions-to-document-interface-to-register\\n * @param {string} type\\n * @param {{extends: (string|undefined), prototype: (Object|undefined)}} options\\n * @deprecated This method has been removed and will be removed soon from this file.\\n */\\nDocument.prototype.register = function(type, options) {};\\n\\n/**\\n * @type {!FontFaceSet}\\n * @see http://dev.w3.org/csswg/css-font-loading/#dom-fontfacesource-fonts\\n */\\nDocument.prototype.fonts;\\n\\n\\n/**\\n * @type {?HTMLScriptElement}\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/currentScript\\n */\\nDocument.prototype.currentScript;\\n\\n/**\\n * Definition of ShadowRoot interface,\\n * @see http://www.w3.org/TR/shadow-dom/#api-shadow-root\\n * @constructor\\n * @extends {DocumentFragment}\\n */\\nfunction ShadowRoot() {}\\n\\n/**\\n * The host element that a ShadowRoot is attached to.\\n * Note: this is not yet W3C standard but is undergoing development.\\n * W3C feature tracking bug:\\n * https://www.w3.org/Bugs/Public/show_bug.cgi?id=22399\\n * Draft specification:\\n * https://dvcs.w3.org/hg/webcomponents/raw-file/6743f1ace623/spec/shadow/index.html#shadow-root-object\\n * @type {!Element}\\n */\\nShadowRoot.prototype.host;\\n\\n/**\\n * @param {string} id id.\\n * @return {HTMLElement}\\n * @nosideeffects\\n */\\nShadowRoot.prototype.getElementById = function(id) {};\\n\\n\\n/**\\n * @param {string} className\\n * @return {!NodeList}\\n * @nosideeffects\\n */\\nShadowRoot.prototype.getElementsByClassName = function(className) {};\\n\\n\\n/**\\n * @param {string} tagName\\n * @return {!NodeList}\\n * @nosideeffects\\n */\\nShadowRoot.prototype.getElementsByTagName = function(tagName) {};\\n\\n\\n/**\\n * @param {string} namespace\\n * @param {string} localName\\n * @return {!NodeList}\\n * @nosideeffects\\n */\\nShadowRoot.prototype.getElementsByTagNameNS = function(namespace, localName) {};\\n\\n\\n/**\\n * @return {Selection}\\n * @nosideeffects\\n */\\nShadowRoot.prototype.getSelection = function() {};\\n\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @return {Element}\\n * @nosideeffects\\n */\\nShadowRoot.prototype.elementFromPoint = function(x, y) {};\\n\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @return {!IArrayLike}\\n * @nosideeffects\\n */\\nShadowRoot.prototype.elementsFromPoint = function(x, y) {};\\n\\n\\n/**\\n * @type {boolean}\\n */\\nShadowRoot.prototype.applyAuthorStyles;\\n\\n\\n/**\\n * @type {boolean}\\n */\\nShadowRoot.prototype.resetStyleInheritance;\\n\\n\\n/**\\n * @type {Element}\\n */\\nShadowRoot.prototype.activeElement;\\n\\n\\n/**\\n * @type {?ShadowRoot}\\n */\\nShadowRoot.prototype.olderShadowRoot;\\n\\n\\n/**\\n * @type {string}\\n * @implicitCast\\n */\\nShadowRoot.prototype.innerHTML;\\n\\n\\n/**\\n * @type {!StyleSheetList}\\n */\\nShadowRoot.prototype.styleSheets;\\n\\n\\n\\n/**\\n * @see http://www.w3.org/TR/shadow-dom/#the-content-element\\n * @constructor\\n * @extends {HTMLElement}\\n */\\nfunction HTMLContentElement() {}\\n\\n/**\\n * @type {!string}\\n */\\nHTMLContentElement.prototype.select;\\n\\n/**\\n * @return {!NodeList}\\n */\\nHTMLContentElement.prototype.getDistributedNodes = function() {};\\n\\n\\n/**\\n * @see http://www.w3.org/TR/shadow-dom/#the-shadow-element\\n * @constructor\\n * @extends {HTMLElement}\\n */\\nfunction HTMLShadowElement() {}\\n\\n/**\\n * @return {!NodeList}\\n */\\nHTMLShadowElement.prototype.getDistributedNodes = function() {};\\n\\n\\n/**\\n * @see http://www.w3.org/TR/html5/webappapis.html#the-errorevent-interface\\n *\\n * @constructor\\n * @extends {Event}\\n *\\n * @param {string} type\\n * @param {ErrorEventInit=} opt_eventInitDict\\n */\\nfunction ErrorEvent(type, opt_eventInitDict) {}\\n\\n/** @const {string} */\\nErrorEvent.prototype.message;\\n\\n/** @const {string} */\\nErrorEvent.prototype.filename;\\n\\n/** @const {number} */\\nErrorEvent.prototype.lineno;\\n\\n/** @const {number} */\\nErrorEvent.prototype.colno;\\n\\n/** @const {*} */\\nErrorEvent.prototype.error;\\n\\n\\n/**\\n * @record\\n * @extends {EventInit}\\n * @see https://www.w3.org/TR/html5/webappapis.html#erroreventinit\\n */\\nfunction ErrorEventInit() {}\\n\\n/** @type {undefined|string} */\\nErrorEventInit.prototype.message;\\n\\n/** @type {undefined|string} */\\nErrorEventInit.prototype.filename;\\n\\n/** @type {undefined|number} */\\nErrorEventInit.prototype.lineno;\\n\\n/** @type {undefined|number} */\\nErrorEventInit.prototype.colno;\\n\\n/** @type {*} */\\nErrorEventInit.prototype.error;\\n\\n\\n/**\\n * @see http://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument\\n * @param {string=} opt_title A title to give the new HTML document\\n * @return {!HTMLDocument}\\n */\\nDOMImplementation.prototype.createHTMLDocument = function(opt_title) {};\\n\\n\\n\\n/**\\n * @constructor\\n * @see https://html.spec.whatwg.org/multipage/embedded-content.html#the-picture-element\\n * @extends {HTMLElement}\\n */\\nfunction HTMLPictureElement() {}\\n\\n/**\\n * @constructor\\n * @see https://html.spec.whatwg.org/multipage/embedded-content.html#the-picture-element\\n * @extends {HTMLElement}\\n */\\nfunction HTMLSourceElement() {}\\n\\n/** @type {string} */\\nHTMLSourceElement.prototype.media;\\n\\n/** @type {string} */\\nHTMLSourceElement.prototype.sizes;\\n\\n/** @type {string} @implicitCast */\\nHTMLSourceElement.prototype.src;\\n\\n/** @type {string} */\\nHTMLSourceElement.prototype.srcset;\\n\\n/** @type {string} */\\nHTMLSourceElement.prototype.type;\\n\\n/** @type {string} */\\nHTMLImageElement.prototype.sizes;\\n\\n/** @type {string} */\\nHTMLImageElement.prototype.srcset;\\n\\n\\n/**\\n * 4.11 Interactive elements\\n * @see http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html\\n */\\n\\n/**\\n * @see http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html#the-details-element\\n * @constructor\\n * @extends {HTMLElement}\\n */\\nfunction HTMLDetailsElement() {}\\n\\n/**\\n * @see http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html#dom-details-open\\n * @type {boolean}\\n */\\nHTMLDetailsElement.prototype.open;\\n\\n\\n// As of 2/20/2015,

      has no special web IDL interface nor global\\n// constructor (i.e. HTMLSummaryElement).\\n\\n\\n/**\\n * @see http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html#dom-menu-type\\n * @type {string}\\n */\\nHTMLMenuElement.prototype.type;\\n\\n/**\\n * @see http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html#dom-menu-label\\n * @type {string}\\n */\\nHTMLMenuElement.prototype.label;\\n\\n\\n/**\\n * @see http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html#the-menuitem-element\\n * @constructor\\n * @extends {HTMLElement}\\n */\\nfunction HTMLMenuItemElement() {}\\n\\n/**\\n * @see http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html#dom-menuitem-type\\n * @type {string}\\n */\\nHTMLMenuItemElement.prototype.type;\\n\\n/**\\n * @see http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html#dom-menuitem-label\\n * @type {string}\\n */\\nHTMLMenuItemElement.prototype.label;\\n\\n/**\\n * @see http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html#dom-menuitem-icon\\n * @type {string}\\n */\\nHTMLMenuItemElement.prototype.icon;\\n\\n/**\\n * @see http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html#dom-menuitem-disabled\\n * @type {boolean}\\n */\\nHTMLMenuItemElement.prototype.disabled;\\n\\n/**\\n * @see http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html#dom-menuitem-checked\\n * @type {boolean}\\n */\\nHTMLMenuItemElement.prototype.checked;\\n\\n/**\\n * @see http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html#dom-menuitem-radiogroup\\n * @type {string}\\n */\\nHTMLMenuItemElement.prototype.radiogroup;\\n\\n/**\\n * @see http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html#dom-menuitem-default\\n * @type {boolean}\\n */\\nHTMLMenuItemElement.prototype.default;\\n\\n// TODO(dbeam): add HTMLMenuItemElement.prototype.command if it's implemented.\\n\\n\\n/**\\n * @see http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html#relatedevent\\n * @param {string} type\\n * @param {{relatedTarget: (EventTarget|undefined)}=} opt_eventInitDict\\n * @constructor\\n * @extends {Event}\\n */\\nfunction RelatedEvent(type, opt_eventInitDict) {}\\n\\n/**\\n * @see http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html#dom-relatedevent-relatedtarget\\n * @type {EventTarget|undefined}\\n */\\nRelatedEvent.prototype.relatedTarget;\\n\\n\\n/**\\n * @see http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html#the-dialog-element\\n * @constructor\\n * @extends {HTMLElement}\\n */\\nfunction HTMLDialogElement() {}\\n\\n/**\\n * @see http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html#dom-dialog-open\\n * @type {boolean}\\n */\\nHTMLDialogElement.prototype.open;\\n\\n/**\\n * @see http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html#dom-dialog-returnvalue\\n * @type {string}\\n */\\nHTMLDialogElement.prototype.returnValue;\\n\\n/**\\n * @see http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html#dom-dialog-show\\n * @param {(MouseEvent|Element)=} opt_anchor\\n * @return {undefined}\\n */\\nHTMLDialogElement.prototype.show = function(opt_anchor) {};\\n\\n/**\\n * @see http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html#dom-dialog-showmodal\\n * @param {(MouseEvent|Element)=} opt_anchor\\n * @return {undefined}\\n */\\nHTMLDialogElement.prototype.showModal = function(opt_anchor) {};\\n\\n/**\\n * @see http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html#dom-dialog-close\\n * @param {string=} opt_returnValue\\n * @return {undefined}\\n */\\nHTMLDialogElement.prototype.close = function(opt_returnValue) {};\\n\\n\\n/**\\n * @see https://html.spec.whatwg.org/multipage/scripting.html#the-template-element\\n * @constructor\\n * @extends {HTMLElement}\\n */\\nfunction HTMLTemplateElement() {}\\n\\n/**\\n * @see https://html.spec.whatwg.org/multipage/scripting.html#the-template-element\\n * @type {!DocumentFragment}\\n */\\nHTMLTemplateElement.prototype.content;\\n\\n\\n/**\\n"; +a.a+=' * @type {?Document}\\n * @see w3c_dom2.js\\n * @see http://www.w3.org/TR/html-imports/#interface-import\\n */\\nHTMLLinkElement.prototype.import;\\n\\n/**\\n * @type {string}\\n * @see https://html.spec.whatwg.org/#attr-link-as\\n * @see https://w3c.github.io/preload/#as-attribute\\n */\\nHTMLLinkElement.prototype.as;\\n\\n\\n/**\\n * @return {boolean}\\n * @see https://www.w3.org/TR/html5/forms.html#dom-fieldset-elements\\n */\\nHTMLFieldSetElement.prototype.checkValidity = function() {};\\n\\n/**\\n * @type {HTMLCollection}\\n * @see https://www.w3.org/TR/html5/forms.html#dom-fieldset-elements\\n */\\nHTMLFieldSetElement.prototype.elements;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/html5/forms.html#the-fieldset-element\\n */\\nHTMLFieldSetElement.prototype.name;\\n\\n/**\\n * @param {string} message\\n * @see https://www.w3.org/TR/html5/forms.html#dom-fieldset-elements\\n * @return {undefined}\\n */\\nHTMLFieldSetElement.prototype.setCustomValidity = function(message) {};\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/html5/forms.html#dom-fieldset-type\\n */\\nHTMLFieldSetElement.prototype.type;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/html5/forms.html#the-fieldset-element\\n */\\nHTMLFieldSetElement.prototype.validationMessage;\\n\\n/**\\n * @type {ValidityState}\\n * @see https://www.w3.org/TR/html5/forms.html#the-fieldset-element\\n */\\nHTMLFieldSetElement.prototype.validity;\\n\\n/**\\n * @type {boolean}\\n * @see https://www.w3.org/TR/html5/forms.html#the-fieldset-element\\n */\\nHTMLFieldSetElement.prototype.willValidate;\\n\\n/**\\n * @constructor\\n * @extends {NodeList}\\n * @template T\\n * @see https://html.spec.whatwg.org/multipage/infrastructure.html#radionodelist\\n */\\nfunction RadioNodeList() {}\\n\\n\\n\\n/**\\n * @see https://html.spec.whatwg.org/multipage/forms.html#the-datalist-element\\n * @constructor\\n * @extends {HTMLElement}\\n */\\nfunction HTMLDataListElement() {}\\n\\n\\n/** @type {HTMLCollection} */\\nHTMLDataListElement.prototype.options;\\n\\n\\n/**\\n * @return {boolean}\\n * @see https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element\\n */\\nHTMLObjectElement.prototype.checkValidity;\\n\\n/**\\n * @param {string} message\\n * @see https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element\\n * @return {undefined}\\n */\\nHTMLObjectElement.prototype.setCustomValidity;\\n\\n/**\\n * @type {string}\\n * @see https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element\\n */\\nHTMLObjectElement.prototype.validationMessage;\\n\\n/**\\n * @type {!ValidityState}\\n * @see https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element\\n */\\nHTMLObjectElement.prototype.validity;\\n\\n/**\\n * @type {boolean}\\n * @see https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element\\n */\\nHTMLObjectElement.prototype.willValidate;\\n\\n\\n/**\\n * @see https://html.spec.whatwg.org/multipage/forms.html#the-output-element\\n * @constructor\\n * @extends {HTMLElement}\\n */\\nfunction HTMLOutputElement() {}\\n\\n/**\\n * @const {!DOMTokenList}\\n */\\nHTMLOutputElement.prototype.htmlFor;\\n\\n/**\\n * @type {HTMLFormElement}\\n */\\nHTMLOutputElement.prototype.form;\\n\\n/**\\n * @type {string}\\n */\\nHTMLOutputElement.prototype.name;\\n\\n/**\\n * @const {string}\\n */\\nHTMLOutputElement.prototype.type;\\n\\n/**\\n * @type {string}\\n */\\nHTMLOutputElement.prototype.defaultValue;\\n\\n/**\\n * @type {string}\\n */\\nHTMLOutputElement.prototype.value;\\n\\n/**\\n * @const {?NodeList}\\n */\\nHTMLOutputElement.prototype.labels;\\n\\n/** @type {string} */\\nHTMLOutputElement.prototype.validationMessage;\\n\\n/**\\n * @const {ValidityState}\\n */\\nHTMLOutputElement.prototype.validity;\\n\\n/** @type {boolean} */\\nHTMLOutputElement.prototype.willValidate;\\n\\n/** @return {boolean} */\\nHTMLOutputElement.prototype.checkValidity = function() {};\\n\\n/** @return {boolean} */\\nHTMLOutputElement.prototype.reportValidity = function() {};\\n\\n/** @param {string} message */\\nHTMLOutputElement.prototype.setCustomValidity = function(message) {};\\n\\n\\n\\n/**\\n * @see https://html.spec.whatwg.org/multipage/forms.html#the-progress-element\\n * @constructor\\n * @extends {HTMLElement}\\n */\\nfunction HTMLProgressElement() {}\\n\\n\\n/** @type {number} */\\nHTMLProgressElement.prototype.value;\\n\\n\\n/** @type {number} */\\nHTMLProgressElement.prototype.max;\\n\\n\\n/** @type {number} */\\nHTMLProgressElement.prototype.position;\\n\\n\\n/** @type {?NodeList} */\\nHTMLProgressElement.prototype.labels;\\n\\n\\n\\n/**\\n * @see https://html.spec.whatwg.org/multipage/embedded-content.html#the-track-element\\n * @constructor\\n * @extends {HTMLElement}\\n */\\nfunction HTMLTrackElement() {}\\n\\n\\n/** @type {string} */\\nHTMLTrackElement.prototype.kind;\\n\\n\\n/** @type {string} @implicitCast */\\nHTMLTrackElement.prototype.src;\\n\\n\\n/** @type {string} */\\nHTMLTrackElement.prototype.srclang;\\n\\n\\n/** @type {string} */\\nHTMLTrackElement.prototype.label;\\n\\n\\n/** @type {boolean} */\\nHTMLTrackElement.prototype.default;\\n\\n\\n/** @const {number} */\\nHTMLTrackElement.prototype.readyState;\\n\\n\\n/** @const {!TextTrack} */\\nHTMLTrackElement.prototype.track;\\n\\n\\n\\n/**\\n * @see https://html.spec.whatwg.org/multipage/forms.html#the-meter-element\\n * @constructor\\n * @extends {HTMLElement}\\n */\\nfunction HTMLMeterElement() {}\\n\\n\\n/** @type {number} */\\nHTMLMeterElement.prototype.value;\\n\\n\\n/** @type {number} */\\nHTMLMeterElement.prototype.min;\\n\\n\\n/** @type {number} */\\nHTMLMeterElement.prototype.max;\\n\\n\\n/** @type {number} */\\nHTMLMeterElement.prototype.low;\\n\\n\\n/** @type {number} */\\nHTMLMeterElement.prototype.high;\\n\\n\\n/** @type {number} */\\nHTMLMeterElement.prototype.optimum;\\n\\n\\n/** @type {?NodeList} */\\nHTMLMeterElement.prototype.labels;\\n\\n\\n/**\\n * @interface\\n * @see https://storage.spec.whatwg.org/#api\\n */\\nfunction NavigatorStorage() {};\\n\\n/**\\n * @type {!StorageManager}\\n */\\nNavigatorStorage.prototype.storage;\\n\\n/**\\n * @constructor\\n * @implements NavigatorStorage\\n * @see https://www.w3.org/TR/html5/webappapis.html#navigator\\n */\\nfunction Navigator() {}\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/html5/webappapis.html#dom-navigator-appcodename\\n */\\nNavigator.prototype.appCodeName;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/html5/webappapis.html#dom-navigator-appname\\n */\\nNavigator.prototype.appName;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/html5/webappapis.html#dom-navigator-appversion\\n */\\nNavigator.prototype.appVersion;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/html5/webappapis.html#dom-navigator-platform\\n */\\nNavigator.prototype.platform;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/html5/webappapis.html#dom-navigator-product\\n */\\nNavigator.prototype.product;\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/html5/webappapis.html#dom-navigator-useragent\\n */\\nNavigator.prototype.userAgent;\\n\\n/**\\n * @return {boolean}\\n * @see https://www.w3.org/TR/html5/webappapis.html#dom-navigator-taintenabled\\n */\\nNavigator.prototype.taintEnabled = function() {};\\n\\n/**\\n * @type {string}\\n * @see https://www.w3.org/TR/html5/webappapis.html#dom-navigator-language\\n */\\nNavigator.prototype.language;\\n\\n/**\\n * @type {boolean}\\n * @see https://www.w3.org/TR/html5/browsers.html#navigatoronline\\n */\\nNavigator.prototype.onLine;\\n\\n/**\\n * @type {boolean}\\n * @see https://www.w3.org/TR/html5/webappapis.html#dom-navigator-cookieenabled\\n */\\nNavigator.prototype.cookieEnabled;\\n\\n/**\\n * @param {string} scheme\\n * @param {string} url\\n * @param {string} title\\n * @return {undefined}\\n */\\nNavigator.prototype.registerProtocolHandler = function(scheme, url, title) {}\\n\\n/**\\n * @param {string} mimeType\\n * @param {string} url\\n * @param {string} title\\n * @return {undefined}\\n */\\nNavigator.prototype.registerContentHandler = function(mimeType, url, title) {}\\n\\n/**\\n * @param {string} scheme\\n * @param {string} url\\n * @return {undefined}\\n */\\nNavigator.prototype.unregisterProtocolHandler = function(scheme, url) {}\\n\\n/**\\n * @param {string} mimeType\\n * @param {string} url\\n * @return {undefined}\\n */\\nNavigator.prototype.unregisterContentHandler = function(mimeType, url) {}\\n\\n/**\\n * @type {!MimeTypeArray}\\n * @see https://www.w3.org/TR/html5/webappapis.html#dom-navigator-mimetypes\\n */\\nNavigator.prototype.mimeTypes;\\n\\n/**\\n * @type {!PluginArray}\\n * @see https://www.w3.org/TR/html5/webappapis.html#dom-navigator-plugins\\n */\\nNavigator.prototype.plugins;\\n\\n/**\\n * @return {boolean}\\n * @see https://www.w3.org/TR/html5/webappapis.html#dom-navigator-javaenabled\\n * @nosideeffects\\n */\\nNavigator.prototype.javaEnabled = function() {};\\n\\n/**\\n * @type {number}\\n * @see https://developers.google.com/web/updates/2017/12/device-memory\\n * https://github.com/w3c/device-memory\\n */\\nNavigator.prototype.deviceMemory;\\n\\n/**\\n * @type {!StorageManager}\\n * @see https://storage.spec.whatwg.org\\n */\\nNavigator.prototype.storage;\\n\\n/**\\n * @param {!ShareData=} data\\n * @return {!Promise}\\n * @see https://wicg.github.io/web-share/#share-method\\n */\\nNavigator.prototype.share = function(data) {};\\n\\n/**\\n * @type {number}\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/NavigatorConcurrentHardware/hardwareConcurrency\\n */\\nNavigator.prototype.hardwareConcurrency;\\n\\n/**\\n * @constructor\\n * @implements NavigatorStorage\\n * @see https://html.spec.whatwg.org/multipage/workers.html#the-workernavigator-object\\n */\\nfunction WorkerNavigator() {}\\n\\n/**\\n * @type {number}\\n * @see https://developers.google.com/web/updates/2017/12/device-memory\\n * https://github.com/w3c/device-memory\\n */\\nWorkerNavigator.prototype.deviceMemory;\\n\\n/**\\n * @type {number}\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/NavigatorConcurrentHardware/hardwareConcurrency\\n */\\nWorkerNavigator.prototype.hardwareConcurrency;\\n\\n/**\\n * @type {!StorageManager}\\n * @see https://storage.spec.whatwg.org\\n */\\nWorkerNavigator.prototype.storage;\\n\\n/**\\n * @record\\n * @see https://wicg.github.io/web-share/#sharedata-dictionary\\n */\\nfunction ShareData() {}\\n\\n/** @type {string|undefined} */\\nShareData.prototype.title;\\n\\n/** @type {string|undefined} */\\nShareData.prototype.text;\\n\\n/** @type {string|undefined} */\\nShareData.prototype.url;\\n\\n/**\\n * @constructor\\n * @implements {IObject<(string|number),!Plugin>}\\n * @implements {IArrayLike}\\n * @see https://www.w3.org/TR/html5/webappapis.html#pluginarray\\n */\\nfunction PluginArray() {}\\n\\n/** @type {number} */\\nPluginArray.prototype.length;\\n\\n/**\\n * @param {number} index\\n * @return {Plugin}\\n */\\nPluginArray.prototype.item = function(index) {};\\n\\n/**\\n * @param {string} name\\n * @return {Plugin}\\n */\\nPluginArray.prototype.namedItem = function(name) {};\\n\\n/**\\n * @param {boolean=} reloadDocuments\\n * @return {undefined}\\n */\\nPluginArray.prototype.refresh = function(reloadDocuments) {};\\n\\n/**\\n * @constructor\\n * @implements {IObject<(string|number),!MimeType>}\\n * @implements {IArrayLike}\\n * @see https://www.w3.org/TR/html5/webappapis.html#mimetypearray\\n */\\nfunction MimeTypeArray() {}\\n\\n/**\\n * @param {number} index\\n * @return {MimeType}\\n */\\nMimeTypeArray.prototype.item = function(index) {};\\n\\n/**\\n * @type {number}\\n * @see https://developer.mozilla.org/en/DOM/window.navigator.mimeTypes\\n */\\nMimeTypeArray.prototype.length;\\n\\n/**\\n * @param {string} name\\n * @return {MimeType}\\n */\\nMimeTypeArray.prototype.namedItem = function(name) {};\\n\\n/**\\n * @constructor\\n * @see https://www.w3.org/TR/html5/webappapis.html#mimetype\\n */\\nfunction MimeType() {}\\n\\n/** @type {string} */\\nMimeType.prototype.description;\\n\\n/** @type {Plugin} */\\nMimeType.prototype.enabledPlugin;\\n\\n/** @type {string} */\\nMimeType.prototype.suffixes;\\n\\n/** @type {string} */\\nMimeType.prototype.type;\\n\\n/**\\n * @constructor\\n * @see https://www.w3.org/TR/html5/webappapis.html#dom-plugin\\n */\\nfunction Plugin() {}\\n\\n/** @type {string} */\\nPlugin.prototype.description;\\n\\n/** @type {string} */\\nPlugin.prototype.filename;\\n\\n/** @type {number} */\\nPlugin.prototype.length;\\n\\n/** @type {string} */\\nPlugin.prototype.name;\\n\\n/**\\n * @see https://html.spec.whatwg.org/multipage/scripting.html#custom-elements\\n * @constructor\\n */\\nfunction CustomElementRegistry() {}\\n\\n/**\\n * @param {string} tagName\\n * @param {!function(new:HTMLElement)} klass\\n * @param {{extends: string}=} options\\n * @return {undefined}\\n */\\nCustomElementRegistry.prototype.define = function (tagName, klass, options) {};\\n\\n/**\\n * @param {string} tagName\\n * @return {?function(new:HTMLElement)}\\n */\\nCustomElementRegistry.prototype.get = function(tagName) {};\\n\\n/**\\n * @param {string} tagName\\n * @return {Promise}\\n */\\nCustomElementRegistry.prototype.whenDefined = function(tagName) {};\\n\\n/**\\n * @param {!Node} root\\n * @return {undefined}\\n */\\nCustomElementRegistry.prototype.upgrade = function(root) {};\\n\\n/** @type {!CustomElementRegistry} */\\nvar customElements;\\n\\n/**\\n * @constructor\\n * @extends {HTMLElement}\\n */\\nfunction HTMLSlotElement() {}\\n\\n/**\\n * @param {!{flatten: boolean}=} options\\n * @return {!Array}\\n */\\nHTMLSlotElement.prototype.assignedNodes = function(options) {};\\n\\n/** @type {boolean} */\\nEvent.prototype.composed;\\n\\n/**\\n * @return {!Array}\\n */\\nEvent.prototype.composedPath = function() {};\\n\\n/**\\n * @constructor\\n * @param {{\\n * firesTouchEvents: (string|undefined),\\n * pointerMovementScrolls: (string|undefined)\\n * }=} opt_options\\n */\\nfunction InputDeviceCapabilities(opt_options){}\\n\\n/** @type {boolean} */\\nInputDeviceCapabilities.prototype.firesTouchEvents;\\n\\n/** @type {boolean} */\\nInputDeviceCapabilities.prototype.pointerMovementScrolls;\\n\\n/** @type {?InputDeviceCapabilities} */\\nMouseEvent.prototype.sourceCapabilities;\\n\\n/**\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/VisualViewport\\n * @constructor\\n * @implements {EventTarget}\\n */\\nfunction VisualViewport() {}\\n\\n/** @type {number} */\\nVisualViewport.prototype.offsetLeft;\\n\\n/** @type {number} */\\nVisualViewport.prototype.offsetTop;\\n\\n/** @type {number} */\\nVisualViewport.prototype.pageLeft;\\n\\n/** @type {number} */\\nVisualViewport.prototype.pageTop;\\n\\n/** @type {number} */\\nVisualViewport.prototype.width;\\n\\n/** @type {number} */\\nVisualViewport.prototype.height;\\n\\n/** @type {number} */\\nVisualViewport.prototype.scale;\\n\\n/** @override */\\nVisualViewport.prototype.addEventListener = function(type, listener,\\n opt_options) {};\\n\\n/** @override */\\nVisualViewport.prototype.removeEventListener = function(type, listener,\\n opt_options) {};\\n\\n/** @override */\\nVisualViewport.prototype.dispatchEvent = function(evt) {};\\n\\n/** @type {?function(!Event)} */\\nVisualViewport.prototype.onresize;\\n\\n/** @type {?function(!Event)} */\\nVisualViewport.prototype.onscroll;\\n\\n/**\\n * @see https://storage.spec.whatwg.org/\\n * @constructor\\n */\\nfunction StorageManager() {}\\n\\n/** @return {!Promise} */\\nStorageManager.prototype.persisted = function() {};\\n\\n/** @return {!Promise} */\\nStorageManager.prototype.persist = function() {};\\n\\n/** @return {!Promise} */\\nStorageManager.prototype.estimate = function() {};\\n\\n/**\\n * @see https://storage.spec.whatwg.org/\\n * @typedef {{\\n * usage: number,\\n * quota: number\\n * }}\\n */\\nvar StorageEstimate;\\n","externs/ie_vml.js":"/*\\n * Copyright 2009 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for IE\'s vector markup language, or VML.\\n *\\n * @externs\\n * @author robbyw@google.com (Robby Walker)\\n */\\n\\n\\n/**\\n * @type {Object|string}\\n * @see http://msdn.microsoft.com/en-us/library/bb263836(VS.85).aspx\\n *'; +a.a+='/\\nElement.prototype.coordorigin;\\n\\n/**\\n * @type {Object|string}\\n * @see http://msdn.microsoft.com/en-us/library/bb263837(VS.85).aspx\\n */\\nElement.prototype.coordsize;\\n\\n/**\\n * @type {string}\\n * @see http://msdn.microsoft.com/en-us/library/bb263839(VS.85).aspx\\n */\\nElement.prototype.fillcolor;\\n\\n/**\\n * @type {boolean}\\n * @see http://msdn.microsoft.com/en-us/library/bb263840(VS.85).aspx\\n */\\nElement.prototype.filled;\\n\\n/**\\n * @type {string}\\n * @see http://msdn.microsoft.com/en-us/library/bb263871(VS.85).aspx\\n */\\nElement.prototype.path;\\n\\n/**\\n * @type {number|string}\\n * @see http://msdn.microsoft.com/en-us/library/bb263877(VS.85).aspx\\n */\\nElement.prototype.rotation;\\n\\n/**\\n * @type {string}\\n * @see http://msdn.microsoft.com/en-us/library/bb263881(VS.85).aspx\\n */\\nElement.prototype.strokecolor;\\n\\n/**\\n * @type {boolean}\\n * @see http://msdn.microsoft.com/en-us/library/bb263882(VS.85).aspx\\n */\\nElement.prototype.stroked;\\n\\n/**\\n * @type {number|string}\\n * @see http://msdn.microsoft.com/en-us/library/bb263883(VS.85).aspx\\n */\\nElement.prototype.strokeweight;\\n","externs/intersection_observer.js":"/*\\n * Copyright 2016 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Externs for Intersection Observer objects.\\n * @see https://wicg.github.io/IntersectionObserver/\\n * @externs\\n * @author robadurbin@google.com (Rob Durbin)\\n */\\n\\n// TODO(robadurbin): Once the Intersection Observer spec is adopted by W3C, add\\n// a w3c_ prefix to this file\'s name.\\n\\n\\n/**\\n * These contain the information provided from a change event.\\n * @see https://wicg.github.io/IntersectionObserver/#intersection-observer-entry\\n * @record\\n */\\nfunction IntersectionObserverEntry() {}\\n\\n/**\\n * The time the change was observed.\\n * @see https://wicg.github.io/IntersectionObserver/#dom-intersectionobserverentry-time\\n * @type {number}\\n * @const\\n */\\nIntersectionObserverEntry.prototype.time;\\n\\n/**\\n * The root intersection rectangle, if target belongs to the same unit of\\n * related similar-origin browsing contexts as the intersection root, null\\n * otherwise.\\n * @see https://wicg.github.io/IntersectionObserver/#dom-intersectionobserverentry-rootbounds\\n * @type {{top: number, right: number, bottom: number, left: number,\\n * height: number, width: number}}\\n * @const\\n */\\nIntersectionObserverEntry.prototype.rootBounds;\\n\\n/**\\n * The rectangle describing the element being observed.\\n * @see https://wicg.github.io/IntersectionObserver/#dom-intersectionobserverentry-boundingclientrect\\n * @type {!{top: number, right: number, bottom: number, left: number,\\n * height: number, width: number}}\\n * @const\\n */\\nIntersectionObserverEntry.prototype.boundingClientRect;\\n\\n/**\\n * The rectangle describing the intersection between the observed element and\\n * the viewport.\\n * @see https://wicg.github.io/IntersectionObserver/#dom-intersectionobserverentry-intersectionrect\\n * @type {!{top: number, right: number, bottom: number, left: number,\\n * height: number, width: number}}\\n * @const\\n */\\nIntersectionObserverEntry.prototype.intersectionRect;\\n\\n/**\\n * Ratio of intersectionRect area to boundingClientRect area.\\n * @see https://wicg.github.io/IntersectionObserver/#dom-intersectionobserverentry-intersectionratio\\n * @type {!number}\\n * @const\\n */\\nIntersectionObserverEntry.prototype.intersectionRatio;\\n\\n/**\\n * The Element whose intersection with the intersection root changed.\\n * @see https://wicg.github.io/IntersectionObserver/#dom-intersectionobserverentry-target\\n * @type {!Element}\\n * @const\\n */\\nIntersectionObserverEntry.prototype.target;\\n\\n/**\\n * Whether or not the target is intersecting with the root.\\n * @see https://wicg.github.io/IntersectionObserver/#dom-intersectionobserverentry-isintersecting\\n * @type {boolean}\\n * @const\\n */\\nIntersectionObserverEntry.prototype.isIntersecting;\\n\\n/**\\n * Callback for the IntersectionObserver.\\n * @see https://wicg.github.io/IntersectionObserver/#intersection-observer-callback\\n * @typedef {function(!Array,!IntersectionObserver)}\\n */\\nvar IntersectionObserverCallback;\\n\\n/**\\n * Options for the IntersectionObserver.\\n * @see https://wicg.github.io/IntersectionObserver/#intersection-observer-init\\n * @typedef {{\\n * threshold: (!Array|number|undefined),\\n * root: (!Element|undefined),\\n * rootMargin: (string|undefined)\\n * }}\\n */\\nvar IntersectionObserverInit;\\n\\n/**\\n * This is the constructor for Intersection Observer objects.\\n * @see https://wicg.github.io/IntersectionObserver/#intersection-observer-interface\\n * @param {!IntersectionObserverCallback} handler The callback for the observer.\\n * @param {!IntersectionObserverInit=} opt_options The object defining the\\n * thresholds, etc.\\n * @constructor\\n */\\nfunction IntersectionObserver(handler, opt_options) {};\\n\\n/**\\n * The root Element to use for intersection, or null if the observer uses the\\n * implicit root.\\n * @see https://wicg.github.io/IntersectionObserver/#dom-intersectionobserver-root\\n * @type {?Element}\\n * @const\\n */\\nIntersectionObserver.prototype.root;\\n\\n/**\\n * Offsets applied to the intersection root\u2019s bounding box, effectively growing\\n * or shrinking the box that is used to calculate intersections.\\n * @see https://wicg.github.io/IntersectionObserver/#dom-intersectionobserver-rootmargin\\n * @type {!string}\\n * @const\\n */\\nIntersectionObserver.prototype.rootMargin;\\n\\n/**\\n * A list of thresholds, sorted in increasing numeric order, where each\\n * threshold is a ratio of intersection area to bounding box area of an observed\\n * target.\\n * @see https://wicg.github.io/IntersectionObserver/#dom-intersectionobserver-thresholds\\n * @type {!Array.}\\n * @const\\n */\\nIntersectionObserver.prototype.thresholds;\\n\\n/**\\n * This is used to set which element to observe.\\n * @see https://wicg.github.io/IntersectionObserver/#dom-intersectionobserver-observe\\n * @param {!Element} element The element to observe.\\n * @return {undefined}\\n */\\nIntersectionObserver.prototype.observe = function(element) {};\\n\\n/**\\n * This is used to stop observing a given element.\\n * @see https://wicg.github.io/IntersectionObserver/#dom-intersectionobserver-unobserve\\n * @param {!Element} element The elmenent to stop observing.\\n * @return {undefined}\\n */\\nIntersectionObserver.prototype.unobserve = function(element) {};\\n\\n/**\\n * Disconnect.\\n * @see https://wicg.github.io/IntersectionObserver/#dom-intersectionobserver-disconnect\\n */\\nIntersectionObserver.prototype.disconnect = function() {};\\n\\n/**\\n * Take records.\\n * @see https://wicg.github.io/IntersectionObserver/#dom-intersectionobserver-takerecords\\n * @return {!Array.}\\n */\\nIntersectionObserver.prototype.takeRecords = function() {};\\n","externs/iphone.js":"/*\\n * Copyright 2009 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n/**\\n * @fileoverview Definitions for all iPhone extensions. Created from:\\n * http://developer.apple.com/library/safari/navigation/\\n *\\n * @externs\\n * @author agrieve@google.com (Andrew Grieve)\\n */\\n\\n\\n/**\\n * @type {number}\\n */\\nTouch.prototype.webkitForce;\\n\\n/**\\n * @type {number}\\n */\\nTouch.prototype.webkitRadiusX;\\n\\n/**\\n * @type {number}\\n */\\nTouch.prototype.webkitRadiusY;\\n\\n/**\\n * The distance between two fingers since the start of an event as a multiplier\\n * of the initial distance. The initial value is 1.0. If less than 1.0, the\\n * gesture is pinch close (to zoom out). If greater than 1.0, the gesture is\\n * pinch open (to zoom in).\\n * @type {number}\\n */\\nTouchEvent.prototype.scale;\\n\\n/**\\n * The delta rotation since the start of an event, in degrees, where clockwise\\n * is positive and counter-clockwise is negative. The initial value is 0.0.\\n * @type {number}\\n */\\nTouchEvent.prototype.rotation;\\n\\n/**\\n * Initializes a newly created TouchEvent object.\\n * @param {string} type\\n * @param {boolean} canBubble\\n * @param {boolean} cancelable\\n * @param {Window} view\\n * @param {number} detail\\n * @param {number} screenX\\n * @param {number} screenY\\n * @param {number} clientX\\n * @param {number} clientY\\n * @param {boolean} ctrlKey\\n * @param {boolean} altKey\\n * @param {boolean} shiftKey\\n * @param {boolean} metaKey\\n * @param {TouchList} touches\\n * @param {TouchList} targetTouches\\n * @param {TouchList} changedTouches\\n * @param {number} scale\\n * @param {number} rotation\\n * @return {undefined}\\n */\\nTouchEvent.prototype.initTouchEvent = function(type, canBubble, cancelable,\\n view, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey,\\n metaKey, touches, targetTouches, changedTouches, scale, rotation) {};\\n\\n/**\\n * The GestureEvent class encapsulates information about a multi-touch gesture.\\n *\\n * GestureEvent objects are high-level events that encapsulate the low-level\\n * TouchEvent objects. Both GestureEvent and TouchEvent events are sent during\\n * a multi-touch sequence. Gesture events contain scaling and rotation\\n * information allowing gestures to be combined, if supported by the platform.\\n * If not supported, one gesture ends before another starts. Listen for\\n * GestureEvent events if you want to respond to gestures only, not process\\n * the low-level TouchEvent objects.\\n *\\n * @see http://developer.apple.com/library/safari/#documentation/UserExperience/Reference/GestureEventClassReference/GestureEvent/GestureEvent.html\\n * @extends {UIEvent}\\n * @constructor\\n */\\nfunction GestureEvent() {}\\n\\n/**\\n * The distance between two fingers since the start of an event as a multiplier\\n * of the initial distance. The initial value is 1.0. If less than 1.0, the\\n * gesture is pinch close (to zoom out). If greater than 1.0, the gesture is\\n * pinch open (to zoom in).\\n * @type {number}\\n */\\nGestureEvent.prototype.scale;\\n\\n/**\\n * The delta rotation since the start of an event, in degrees, where clockwise\\n * is positive and counter-clockwise is negative. The initial value is 0.0.\\n * @type {number}\\n */\\nGestureEvent.prototype.rotation;\\n\\n/**\\n * The target of this gesture.\\n * @type {EventTarget}\\n */\\nGestureEvent.prototype.target;\\n\\n/**\\n * Initializes a newly created GestureEvent object.\\n * @param {string} type\\n * @param {boolean} canBubble\\n * @param {boolean} cancelable\\n * @param {Window} view\\n * @param {number} detail\\n * @param {number} screenX\\n * @param {number} screenY\\n * @param {number} clientX\\n * @param {number} clientY\\n * @param {boolean} ctrlKey\\n * @param {boolean} altKey\\n * @param {boolean} shiftKey\\n * @param {boolean} metaKey\\n * @param {EventTarget} target\\n * @param {number} scale\\n * @param {number} rotation\\n * @return {undefined}\\n */\\nGestureEvent.prototype.initGestureEvent = function(type, canBubble, cancelable,\\n view, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey,\\n metaKey, target, scale, rotation) {};\\n\\n\\n/**\\n * Specifies the JavaScript method to invoke when a gesture is started by\\n * two or more fingers touching the surface.\\n * @type {?function(!GestureEvent)}\\n */\\nElement.prototype.ongesturestart;\\n\\n/**\\n * Specifies the JavaScript method to invoke when fingers are moved during a\\n * gesture.\\n * @type {?function(!GestureEvent)}\\n */\\nElement.prototype.ongesturechange;\\n\\n/**\\n * Specifies the JavaScript method to invoke when a gesture ends (when there are\\n * 0 or 1 fingers touching the surface).\\n * @type {?function(!GestureEvent)}\\n */\\nElement.prototype.ongestureend;\\n\\n/**\\n * Specifies the JavaScript method to invoke when the browser device\'s\\n * orientation changes, i.e.the device is rotated.\\n * @type {?function(!Event)}\\n * @see http://developer.apple.com/library/IOS/#documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html\\n */\\nWindow.prototype.onorientationchange;\\n\\n/**\\n * Returns the orientation of the browser\'s device, one of [-90, 0, 90, 180].\\n * @type {number}\\n * @see http://developer.apple.com/library/IOS/#documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html\\n */\\nWindow.prototype.orientation;\\n\\n/**\\n * @implicitCast\\n * @type {boolean}\\n */\\nHTMLInputElement.prototype.autocorrect;\\n\\n/**\\n * @implicitCast\\n * @type {boolean}\\n */\\nHTMLInputElement.prototype.autocapitalize;\\n\\n/**\\n * @implicitCast\\n * @type {boolean}\\n */\\nHTMLTextAreaElement.prototype.autocorrect;\\n\\n/**\\n * @implicitCast\\n * @type {boolean}\\n */\\nHTMLTextAreaElement.prototype.autocapitalize;\\n","externs/mediakeys.js":"/*\\n * Copyright 2015 The Closure Compiler authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n/**\\n * @fileoverview MediaKey externs.\\n * Based on {@link http://goo.gl/blgtZZ EME draft 12 March 2015}.\\n * @externs\\n */\\n\\n\\n/**\\n * @typedef {{contentType: string, robustness: string}}\\n * @see https://w3c.github.io/encrypted-media/#mediakeysystemmediacapability-dictionary\\n */\\nvar MediaKeySystemMediaCapability;\\n\\n\\n/** @typedef {{\\n * label: (string|undefined),\\n * initDataTypes: (!Array|undefined),\\n * audioCapabilities: (!Array|undefined),\\n * videoCapabilities: (!Array|undefined),\\n * distinctiveIdentifier: (string|undefined),\\n * persistentState: (string|undefined),\\n * sessionTypes: (!Array|undefined)\\n * }}\\n * @see https://w3c.github.io/encrypted-media/#mediakeysystemconfiguration-dictionary\\n */\\nvar MediaKeySystemConfiguration;\\n\\n\\n/**\\n * @param {string} keySystem\\n * @param {!Array} supportedConfigurations\\n * @return {!Promise}\\n * @see https://w3c.github.io/encrypted-media/#navigator-extension-requestmediakeysystemaccess\\n */\\nNavigator.prototype.requestMediaKeySystemAccess =\\n function(keySystem, supportedConfigurations) {};\\n\\n\\n/** @const {MediaKeys} */\\nHTMLMediaElement.prototype.mediaKeys;\\n\\n\\n/**\\n * @param {MediaKeys} mediaKeys\\n * @return {!Promise}\\n * @see https://w3c.github.io/encrypted-media/#widl-HTMLMediaElement-setMediaKeys-Promise-void--MediaKeys-mediaKeys\\n */\\nHTMLMediaElement.prototype.setMediaKeys = function(mediaKeys) {};\\n\\n\\n\\n/**\\n * @interface\\n * @see https://w3c.github.io/encrypted-media/#mediakeysystemaccess-interface\\n */\\nfunction MediaKeySystemAccess() {}\\n\\n\\n/** @return {!Promise} */\\nMediaKeySystemAccess.prototype.createMediaKeys = function() {};\\n\\n\\n/** @return {!MediaKeySystemConfiguration} */\\nMediaKeySystemAccess.prototype.getConfiguration = function() {};\\n\\n\\n/** @const {string} */\\nMediaKeySystemAccess.prototype.keySystem;\\n\\n\\n\\n/**\\n * @interface\\n * @see https://w3c.github.io/encrypted-media/#mediakeys-interface\\n */\\nfun'; +a.a+='ction MediaKeys() {}\\n\\n\\n/**\\n * @param {string=} opt_sessionType defaults to \\"temporary\\"\\n * @return {!MediaKeySession}\\n * @throws {TypeError} if opt_sessionType is invalid.\\n */\\nMediaKeys.prototype.createSession = function(opt_sessionType) {};\\n\\n\\n/**\\n * @param {!BufferSource} serverCertificate\\n * @return {!Promise}\\n */\\nMediaKeys.prototype.setServerCertificate = function(serverCertificate) {};\\n\\n\\n\\n/**\\n * @interface\\n * @see https://w3c.github.io/encrypted-media/#mediakeystatusmap-interface\\n */\\nfunction MediaKeyStatusMap() {}\\n\\n\\n/** @const {number} */\\nMediaKeyStatusMap.prototype.size;\\n\\n\\n/**\\n * Array entry 0 is the key, 1 is the value.\\n * @return {!Iterator>}\\n */\\nMediaKeyStatusMap.prototype.entries = function() {};\\n\\n\\n/**\\n * The function is called with each value.\\n * @param {function(string, !BufferSource)} callback A callback function to run for\\n * each media key. The first parameter is the key status; the second\\n * parameter is the key ID.\\n * @return {undefined}\\n */\\nMediaKeyStatusMap.prototype.forEach = function(callback) {};\\n\\n\\n/**\\n * @param {!BufferSource} keyId\\n * @return {string|undefined}\\n */\\nMediaKeyStatusMap.prototype.get = function(keyId) {};\\n\\n\\n/**\\n * @param {!BufferSource} keyId\\n * @return {boolean}\\n */\\nMediaKeyStatusMap.prototype.has = function(keyId) {};\\n\\n\\n/**\\n * @return {!Iterator}\\n */\\nMediaKeyStatusMap.prototype.keys = function() {};\\n\\n\\n/**\\n * @return {!Iterator}\\n */\\nMediaKeyStatusMap.prototype.values = function() {};\\n\\n\\n\\n/**\\n * @interface\\n * @extends {EventTarget}\\n * @see https://w3c.github.io/encrypted-media/#mediakeysession-interface\\n */\\nfunction MediaKeySession() {}\\n\\n\\n/** @const {string} */\\nMediaKeySession.prototype.sessionId;\\n\\n\\n/** @const {number} */\\nMediaKeySession.prototype.expiration;\\n\\n\\n/** @const {!Promise} */\\nMediaKeySession.prototype.closed;\\n\\n\\n/** @const {!MediaKeyStatusMap} */\\nMediaKeySession.prototype.keyStatuses;\\n\\n\\n/**\\n * @param {string} initDataType\\n * @param {!BufferSource} initData\\n * @return {!Promise}\\n */\\nMediaKeySession.prototype.generateRequest = function(initDataType, initData) {};\\n\\n\\n/**\\n * @param {string} sessionId\\n * @return {!Promise}}\\n */\\nMediaKeySession.prototype.load = function(sessionId) {};\\n\\n\\n/**\\n * @param {!BufferSource} response\\n * @return {!Promise}\\n */\\nMediaKeySession.prototype.update = function(response) {};\\n\\n\\n/** @return {!Promise} */\\nMediaKeySession.prototype.close = function() {};\\n\\n\\n/** @return {!Promise} */\\nMediaKeySession.prototype.remove = function() {};\\n\\n\\n/**\\n * @override\\n */\\nMediaKeySession.prototype.addEventListener = function(\\n type, listener, opt_options) {};\\n\\n\\n/**\\n * @override\\n */\\nMediaKeySession.prototype.removeEventListener = function(\\n type, listener, opt_options) {};\\n\\n\\n/**\\n * @override\\n * @return {boolean}\\n */\\nMediaKeySession.prototype.dispatchEvent = function(evt) {};\\n\\n/**\\n * @record\\n * @extends {EventInit}\\n */\\nfunction MediaKeyMessageEventInit() {};\\n\\n/** @type {string} */\\nMediaKeyMessageEventInit.prototype.messageType;\\n\\n/** @type {!ArrayBuffer} */\\nMediaKeyMessageEventInit.prototype.message;\\n\\n\\n/**\\n * @constructor\\n * @param {string} type\\n * @param {MediaKeyMessageEventInit} eventInitDict\\n * @extends {Event}\\n * @see https://w3c.github.io/encrypted-media/#mediakeymessageevent\\n */\\nfunction MediaKeyMessageEvent(type, eventInitDict) {}\\n\\n\\n/** @const {string} */\\nMediaKeyMessageEvent.prototype.messageType;\\n\\n\\n/** @const {!ArrayBuffer} */\\nMediaKeyMessageEvent.prototype.message;\\n\\n\\n/** @const {!MediaKeySession} */\\nMediaKeyMessageEvent.prototype.target;\\n\\n/**\\n * @record\\n * @extends {EventInit}\\n */\\nfunction MediaEncryptedEventInit() {};\\n\\n/** @type {(string | undefined)} */\\nMediaEncryptedEventInit.prototype.initDataType;\\n\\n/** @type {(ArrayBuffer | undefined)} */\\nMediaEncryptedEventInit.prototype.initData;\\n\\n/**\\n * @constructor\\n * @param {string} type\\n * @param {MediaEncryptedEventInit=} opt_eventInitDict\\n * @extends {Event}\\n * @see https://w3c.github.io/encrypted-media/#mediaencryptedevent\\n */\\nfunction MediaEncryptedEvent(type, opt_eventInitDict) {}\\n\\n\\n/** @const {string} */\\nMediaEncryptedEvent.prototype.initDataType;\\n\\n\\n/** @const {ArrayBuffer} */\\nMediaEncryptedEvent.prototype.initData;\\n\\n\\n/** @const {!HTMLMediaElement} */\\nMediaEncryptedEvent.prototype.target;\\n\\n","externs/mediasource.js":"/*\\n * Copyright 2012 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n/**\\n * @fileoverview Definitions for the Media Source Extensions. Note that the\\n * properties available here are the union of several versions of the spec.\\n * @see http://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html\\n *\\n * @externs\\n * @author mattward@google.com (Matt Ward)\\n */\\n\\n/**\\n * @constructor\\n * @implements {EventTarget}\\n */\\nfunction MediaSource() {}\\n\\n/** @override */\\nMediaSource.prototype.addEventListener = function(type, listener, opt_options) {\\n};\\n\\n/** @override */\\nMediaSource.prototype.removeEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nMediaSource.prototype.dispatchEvent = function(evt) {};\\n\\n/** @type {Array} */\\nMediaSource.prototype.sourceBuffers;\\n\\n/** @type {Array} */\\nMediaSource.prototype.activeSourceBuffers;\\n\\n/** @type {number} */\\nMediaSource.prototype.duration;\\n\\n/**\\n * @param {string} type\\n * @return {SourceBuffer}\\n */\\nMediaSource.prototype.addSourceBuffer = function(type) {};\\n\\n/**\\n * @param {SourceBuffer} sourceBuffer\\n * @return {undefined}\\n */\\nMediaSource.prototype.removeSourceBuffer = function(sourceBuffer) {};\\n\\n/**\\n * Updates the live seekable range.\\n * @param {number} start\\n * @param {number} end\\n */\\nMediaSource.prototype.setLiveSeekableRange = function(start, end) {};\\n\\n/**\\n * Clears the live seekable range.\\n * @return {void}\\n */\\nMediaSource.prototype.clearLiveSeekableRange = function() {};\\n\\n/** @type {string} */\\nMediaSource.prototype.readyState;\\n\\n/**\\n * @param {string=} opt_error\\n * @return {undefined}\\n */\\nMediaSource.prototype.endOfStream = function(opt_error) {};\\n\\n/**\\n * @param {string} type\\n * @return {boolean}\\n */\\nMediaSource.isTypeSupported = function(type) {};\\n\\n\\n/**\\n * @constructor\\n * @implements {EventTarget}\\n */\\nfunction SourceBuffer() {}\\n\\n/** @override */\\nSourceBuffer.prototype.addEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nSourceBuffer.prototype.removeEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nSourceBuffer.prototype.dispatchEvent = function(evt) {};\\n\\n/** @type {string} */\\nSourceBuffer.prototype.appendMode;\\n\\n/** @type {boolean} */\\nSourceBuffer.prototype.updating;\\n\\n/** @type {TimeRanges} */\\nSourceBuffer.prototype.buffered;\\n\\n/** @type {number} */\\nSourceBuffer.prototype.timestampOffset;\\n\\n/** @type {number} */\\nSourceBuffer.prototype.appendWindowStart;\\n\\n/** @type {number} */\\nSourceBuffer.prototype.appendWindowEnd;\\n\\n/**\\n * @param {Uint8Array} data\\n * @return {undefined}\\n */\\nSourceBuffer.prototype.append = function(data) {};\\n\\n/**\\n * @param {ArrayBuffer|ArrayBufferView} data\\n * @return {undefined}\\n */\\nSourceBuffer.prototype.appendBuffer = function(data) {};\\n\\n/**\\n * Abort the current segment append sequence.\\n * @return {undefined}\\n */\\nSourceBuffer.prototype.abort = function() {};\\n\\n/**\\n * @param {number} start\\n * @param {number} end\\n * @return {undefined}\\n */\\nSourceBuffer.prototype.remove = function(start, end) {};\\n","externs/page_visibility.js":"/*\\n * Copyright 2015 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n/**\\n * @fileoverview Externs for Page Visibility.\\n *\\n * @see http://www.w3.org/TR/page-visibility\\n * @externs\\n */\\n\\n/**\\n * Set of possible values: \'hidden\', \'visible\', \'prerender\', \'unloaded\'.\\n * @typedef {string}\\n * @see http://www.w3.org/TR/page-visibility/#VisibilityState\\n */\\nvar VisibilityState;\\n","externs/url.js":"/*\\n * Copyright 2015 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for URL and URLSearchParams from the spec at\\n * https://url.spec.whatwg.org.\\n *\\n * @externs\\n * @author rdcronin@google.com (Devlin Cronin)\\n */\\n\\n/**\\n * @typedef {Array}\\n */\\nvar URLSearchParamsTupleType;\\n\\n/**\\n * Represents the query string of a URL.\\n *\\n * * When `init` is a string, it is basically parsed as a query string\\n * `\'name1=value1&name2=value2\'`.\\n *\\n * * When `init` is an array of arrays of string\\n * `([[\'name1\', \'value1\'], [\'name2\', \'value2\']])`,\\n * it must contain pairs of strings, where the first item in the pair will be\\n * interpreted as a key and the second as a value.\\n *\\n * NOTE: The specification uses Iterable rather than Array, but this is not\\n * supported in Edge 17 - 18.\\n *\\n * * When `init` is an object, keys and values will be interpreted as such\\n * `({name1: \'value1\', name2: \'value2\'}).\\n *\\n * @see https://url.spec.whatwg.org/#interface-urlsearchparams\\n * @constructor\\n * @implements {Iterable>}\\n * @param {(string|!Array|!Object)=}\\n * init\\n */\\nfunction URLSearchParams(init) {}\\n\\n/**\\n * @param {string} name\\n * @param {string} value\\n * @return {undefined}\\n */\\nURLSearchParams.prototype.append = function(name, value) {};\\n\\n/**\\n * @param {string} name\\n * @return {undefined}\\n */\\nURLSearchParams.prototype.delete = function(name) {};\\n\\n/**\\n * @param {string} name\\n * @return {?string}\\n */\\nURLSearchParams.prototype.get = function(name) {};\\n\\n/**\\n * @param {string} name\\n * @return {!Array}\\n */\\nURLSearchParams.prototype.getAll = function(name) {};\\n\\n/**\\n * @param {string} name\\n * @return {boolean}\\n */\\nURLSearchParams.prototype.has = function(name) {};\\n\\n/**\\n * @param {string} name\\n * @param {string} value\\n * @return {undefined}\\n */\\nURLSearchParams.prototype.set = function(name, value) {};\\n\\n/**\\n * @return {undefined}\\n */\\nURLSearchParams.prototype.sort = function() {};\\n\\n/**\\n * @see https://url.spec.whatwg.org\\n * @constructor\\n * @param {string} url\\n * @param {(string|!URL)=} base\\n */\\nfunction URL(url, base) {}\\n\\n/** @type {string} */\\nURL.prototype.href;\\n\\n/**\\n * @const\\n * @type {string}\\n */\\nURL.prototype.origin;\\n\\n/** @type {string} */\\nURL.prototype.protocol;\\n\\n/** @type {string} */\\nURL.prototype.username;\\n\\n/** @type {string} */\\nURL.prototype.password;\\n\\n/** @type {string} */\\nURL.prototype.host;\\n\\n/** @type {string} */\\nURL.prototype.hostname;\\n\\n/** @type {string} */\\nURL.prototype.port;\\n\\n/** @type {string} */\\nURL.prototype.pathname;\\n\\n/** @type {string} */\\nURL.prototype.search;\\n\\n/**\\n * @const\\n * @type {!URLSearchParams}\\n */\\nURL.prototype.searchParams;\\n\\n/** @type {string} */\\nURL.prototype.hash;\\n\\n/**\\n * @param {string} domain\\n * @return {string}\\n */\\nURL.domainToASCII = function(domain) {};\\n\\n/**\\n * @param {string} domain\\n * @return {string}\\n */\\nURL.domainToUnicode = function(domain) {};\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-createObjectURL\\n * @param {!File|!Blob|!MediaSource|!MediaStream} obj\\n * @return {string}\\n */\\nURL.createObjectURL = function(obj) {};\\n\\n/**\\n * @see http://www.w3.org/TR/FileAPI/#dfn-revokeObjectURL\\n * @param {string} url\\n * @return {undefined}\\n */\\nURL.revokeObjectURL = function(url) {};\\n","externs/v8.js":"/*\\n * Copyright 2013 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview This file describes the externs API for V8-specific objects.\\n * @externs\\n */\\n\\n\\n\\n/**\\n * Stack frame elements in V8.\\n * @constructor\\n */\\nfunction CallSite() {}\\n\\n\\n/**\\n * Returns the value of this.\\n * @return {Object|undefined}\\n */\\nCallSite.prototype.getThis = function() {};\\n\\n\\n/**\\n * Returns the type of this as a string. This is the name of the function stored\\n * in the constructor field of this, if available, otherwise the object\'s\\n * [[Class]] internal property.\\n * @return {string|undefined}\\n */\\nCallSite.prototype.getTypeName = function() {};\\n\\n\\n/**\\n * Returns the current function.\\n * @return {!Function|undefined}\\n */\\nCallSite.prototype.getFunction = function() {};\\n\\n\\n/**\\n * Returns the name of the current function, typically its name property. If a\\n * name property is not available an attempt will be made to try to infer a name\\n * from the function\'s context.\\n * @return {string|undefined}\\n */\\nCallSite.prototype.getFunctionName = function() {};\\n\\n\\n/**\\n * Returns the name of the property of this or one of its prototypes that holds\\n * the current function.\\n * @return {string|undefined}\\n */\\nCallSite.prototype.getMethodName = function() {};\\n\\n\\n/**\\n * If this function was defined in a script returns the name of the script\\n * @return {string|undefined}\\n */\\nCallSite.prototype.getFileName = function() {};\\n\\n\\n/**\\n * If this function was defined in a script returns the current line number.\\n * @return {number|undefined}\\n */\\nCallSite.prototype.getLineNumber = function() {};\\n\\n\\n/**\\n * If this function was defined in a script returns the current column number.\\n * @return {number|undefined}\\n */\\nCallSite.prototype.getColumnNumber = function() {};\\n\\n\\n/**\\n * If this function was created using a call to eval, returns a CallSite object\\n * representing the location where eval was called\\n * @return {CallSite|undefined}\\n */\\nCallSite.prototype.getEvalOrigin = function() {};\\n\\n\\n/**\\n * Is this a toplevel invocation, that is, is this the global object?\\n * @return {boolean}\\n */\\nCallSite.prototype.isToplevel = function() {};\\n\\n\\n/**\\n * Does this call take place in code defined by a call to eval?\\n * @return {boolean}\\n */\\nCallSite.prototype.isEval = function() {};\\n\\n\\n/**\\n * Is this call in native V8 code?\\n * @return {boolean}\\n */\\nCallSite.prototype.isNative = function() {};\\n\\n\\n/**\\n * Is this a constructor call?\\n * @return {boolean}\\n */\\nCallSite.prototype.isConstructor = function() {};\\n","externs/webstorage.js":"/*\\n * Copyright 2009 T'; +a.a+='he Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n/**\\n * @fileoverview Definitions for W3C\'s WebStorage specification.\\n * This file depends on html5.js.\\n * @externs\\n * @author jeffbailey@google.com (Jeff Bailey)\\n */\\n\\n/**\\n * @interface\\n * @see http://www.w3.org/TR/2011/CR-webstorage-20111208/#the-storage-interface\\n */\\nfunction Storage() {}\\n\\n/**\\n * @type {number}\\n * @const\\n */\\nStorage.prototype.length;\\n\\n/**\\n * @param {number} index\\n * @return {?string}\\n */\\nStorage.prototype.key = function(index) {};\\n\\n/**\\n * @param {string} key\\n * @return {?string}\\n */\\nStorage.prototype.getItem = function(key) {};\\n\\n/**\\n * @param {string} key\\n * @param {string} data\\n * @return {void}\\n */\\nStorage.prototype.setItem = function(key, data) {};\\n\\n/**\\n * @param {string} key\\n * @return {void}\\n */\\nStorage.prototype.removeItem = function(key) {};\\n\\n/**\\n * @return {void}\\n */\\nStorage.prototype.clear = function() {};\\n\\n/**\\n * @interface\\n * @see http://www.w3.org/TR/2011/CR-webstorage-20111208/#the-sessionstorage-attribute\\n */\\nfunction WindowSessionStorage() {}\\n\\n/**\\n * @type {Storage}\\n */\\nWindowSessionStorage.prototype.sessionStorage;\\n\\n/**\\n * Window implements WindowSessionStorage\\n *\\n * @type {Storage}\\n */\\nWindow.prototype.sessionStorage;\\n\\n/**\\n * @interface\\n * @see http://www.w3.org/TR/2011/CR-webstorage-20111208/#the-localstorage-attribute\\n */\\nfunction WindowLocalStorage() {}\\n\\n/**\\n * @type {Storage}\\n */\\nWindowLocalStorage.prototype.localStorage;\\n\\n/**\\n * Window implements WindowLocalStorage\\n *\\n * @type {Storage}\\n */\\nWindow.prototype.localStorage;\\n\\n/**\\n * This is the storage event interface.\\n * @see http://www.w3.org/TR/2011/CR-webstorage-20111208/#the-storage-event\\n * @extends {Event}\\n * @constructor\\n */\\nfunction StorageEvent() {}\\n\\n/**\\n * @type {string}\\n */\\nStorageEvent.prototype.key;\\n\\n/**\\n * @type {?string}\\n */\\nStorageEvent.prototype.oldValue;\\n\\n/**\\n * @type {?string}\\n */\\nStorageEvent.prototype.newValue;\\n\\n/**\\n * @type {string}\\n */\\nStorageEvent.prototype.url;\\n\\n/**\\n * @type {?Storage}\\n */\\nStorageEvent.prototype.storageArea;\\n\\n/**\\n * @param {string} typeArg\\n * @param {boolean} canBubbleArg\\n * @param {boolean} cancelableArg\\n * @param {string} keyArg\\n * @param {?string} oldValueArg\\n * @param {?string} newValueArg\\n * @param {string} urlArg\\n * @param {?Storage} storageAreaArg\\n * @return {void}\\n */\\nStorageEvent.prototype.initStorageEvent = function(typeArg, canBubbleArg,\\n cancelableArg, keyArg,\\n oldValueArg, newValueArg,\\n urlArg, storageAreaArg) {};\\n\\n","externs/whatwg_encoding.js":"/*\\n * Copyright 2015 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for WHATWG\'s Encoding specification\\n * https://encoding.spec.whatwg.org\\n * @externs\\n */\\n\\n/**\\n * @constructor\\n * @param {string=} encoding\\n * @param {Object=} options\\n */\\nfunction TextDecoder(encoding, options) {}\\n\\n/** @type {string} **/ TextDecoder.prototype.encoding;\\n/** @type {boolean} **/ TextDecoder.prototype.fatal;\\n/** @type {boolean} **/ TextDecoder.prototype.ignoreBOM;\\n\\n/**\\n * @param {!BufferSource=} input\\n * @param {?Object=} options\\n * @return {!string}\\n * @see https://encoding.spec.whatwg.org/#textdecoder\\n */\\nTextDecoder.prototype.decode = function decode(input, options) {};\\n\\n/**\\n * @constructor\\n * @param {string=} utfLabel\\n */\\nfunction TextEncoder(utfLabel) {}\\n\\n/** @type {string} **/ TextEncoder.prototype.encoding;\\n\\n/**\\n * @param {string=} input\\n * @return {!Uint8Array}\\n */\\nTextEncoder.prototype.encode = function(input) {};\\n","externs/w3c_abort.js":"/*\\n * Copyright 2018 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for AbortController\\n * @see https://dom.spec.whatwg.org/#aborting-ongoing-activities\\n * @externs\\n */\\n\\n\\n\\n/**\\n * @record\\n * @extends {EventTarget}\\n * @see https://dom.spec.whatwg.org/#interface-AbortSignal\\n */\\nfunction AbortSignal() {}\\n\\n/** @type {boolean} */\\nAbortSignal.prototype.aborted;\\n\\n/** @type {?function(!Event)} */\\nAbortSignal.prototype.onabort;\\n\\n\\n\\n/**\\n * @constructor\\n * @see https://dom.spec.whatwg.org/#interface-abortcontroller\\n */\\nfunction AbortController() {}\\n\\n/** @const {!AbortSignal} */\\nAbortController.prototype.signal;\\n\\n/**\\n * @const\\n * @return {void}\\n */\\nAbortController.prototype.abort = function() {};\\n","externs/w3c_anim_timing.js":"/*\\n * Copyright 2011 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n/**\\n * @fileoverview Definitions for timing control for script base animations. The\\n * whole file has been fully type annotated.\\n *\\n * @see http://www.w3.org/TR/animation-timing/\\n * @see http://webstuff.nfshost.com/anim-timing/Overview.html\\n * @externs\\n * @author bcornell@google.com (Brian Cornell)\\n */\\n\\n/**\\n * @param {function(number): undefined} callback\\n * @param {Element=} opt_element In early versions of this API, the callback\\n * was invoked only if the element was visible.\\n * @return {number}\\n */\\nfunction requestAnimationFrame(callback, opt_element) {};\\n\\n/**\\n * @param {number} handle\\n * @return {undefined}\\n */\\nfunction cancelRequestAnimationFrame(handle) {};\\n\\n/**\\n * @param {number} handle\\n * @return {undefined}\\n */\\nfunction cancelAnimationFrame(handle) {};\\n\\n/**\\n * @param {function(number)} callback\\n * @param {Element=} opt_element\\n * @return {number}\\n */\\nfunction webkitRequestAnimationFrame(callback, opt_element) {};\\n\\n/**\\n * @param {number} handle\\n * @return {undefined}\\n */\\nfunction webkitCancelRequestAnimationFrame(handle) {};\\n\\n/**\\n * @param {number} handle\\n * @return {undefined}\\n */\\nfunction webkitCancelAnimationFrame(handle) {};\\n\\n/**\\n * @param {?function(number)} callback It\'s legitimate to pass a null\\n * callback and listen on the MozBeforePaint event instead.\\n * @param {Element=} opt_element\\n * @return {number}\\n */\\nfunction mozRequestAnimationFrame(callback, opt_element) {};\\n\\n/**\\n * @param {number} handle\\n * @return {undefined}\\n */\\nfunction mozCancelRequestAnimationFrame(handle) {};\\n\\n/**\\n * @param {number} handle\\n * @return {undefined}\\n */\\nfunction mozCancelAnimationFrame(handle) {};\\n\\n/**\\n * @param {function(number)} callback\\n * @param {Element=} opt_element\\n * @return {number}\\n */\\nfunction msRequestAnimationFrame(callback, opt_element) {};\\n\\n/**\\n * @param {number} handle\\n * @return {undefined}\\n */\\nfunction msCancelRequestAnimationFrame(handle) {};\\n\\n/**\\n * @param {number} handle\\n * @return {undefined}\\n */\\nfunction msCancelAnimationFrame(handle) {};\\n\\n/**\\n * @param {function(number)} callback\\n * @param {Element=} opt_element\\n * @return {number}\\n */\\nfunction oRequestAnimationFrame(callback, opt_element) {};\\n\\n/**\\n * @param {number} handle\\n * @return {undefined}\\n */\\nfunction oCancelRequestAnimationFrame(handle) {};\\n\\n/**\\n * @param {number} handle\\n * @return {undefined}\\n */\\nfunction oCancelAnimationFrame(handle) {};\\n","externs/w3c_audio.js":"/*\\n * Copyright 2012 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for the API related to audio.\\n * Definitions for the Web Audio API.\\n * This file is based on the W3C Working Draft 08 December 2015.\\n * @see http://www.w3.org/TR/webaudio/\\n *\\n * @externs\\n */\\n\\n/**\\n * @implements {EventTarget}\\n * @constructor\\n */\\nfunction BaseAudioContext() {}\\n\\n/** @type {!AudioDestinationNode} */\\nBaseAudioContext.prototype.destination;\\n\\n/** @type {number} */\\nBaseAudioContext.prototype.sampleRate;\\n\\n/** @type {number} */\\nBaseAudioContext.prototype.currentTime;\\n\\n/** @type {!AudioListener} */\\nBaseAudioContext.prototype.listener;\\n\\n/**\\n * @type {string}\\n * See https://www.w3.org/TR/webaudio/#BaseAudioContext for valid values\\n */\\nBaseAudioContext.prototype.state;\\n\\n/**\\n * @param {number} numberOfChannels\\n * @param {number} length\\n * @param {number} sampleRate\\n * @return {!AudioBuffer}\\n */\\nBaseAudioContext.prototype.createBuffer =\\n function(numberOfChannels, length, sampleRate) {};\\n\\n/**\\n * @param {!ArrayBuffer} audioData\\n * @param {function(!AudioBuffer)=} successCallback\\n * @param {function(?)=} errorCallback\\n * @return {!Promise}\\n */\\nBaseAudioContext.prototype.decodeAudioData =\\n function(audioData, successCallback, errorCallback) {};\\n\\n/**\\n * @return {!AudioBufferSourceNode}\\n */\\nBaseAudioContext.prototype.createBufferSource = function() {};\\n\\n/**\\n * @deprecated Use createAudioWorker instead\\n * @param {number=} bufferSize\\n * @param {number=} numberOfInputChannels_opt\\n * @param {number=} numberOfOutputChannels_opt\\n * @return {!ScriptProcessorNode}\\n */\\nBaseAudioContext.prototype.createScriptProcessor = function(bufferSize,\\n numberOfInputChannels_opt, numberOfOutputChannels_opt) {};\\n\\n/**\\n * @return {!AnalyserNode}\\n */\\nBaseAudioContext.prototype.createAnalyser = function() {};\\n\\n/**\\n * @return {!GainNode}\\n */\\nBaseAudioContext.prototype.createGain = function() {};\\n\\n/**\\n * @param {number=} maxDelayTime\\n * @return {!DelayNode}\\n */\\nBaseAudioContext.prototype.createDelay = function(maxDelayTime) {};\\n\\n/**\\n * @return {!BiquadFilterNode}\\n */\\nBaseAudioContext.prototype.createBiquadFilter = function() {};\\n\\n/**\\n * @return {!WaveShaperNode}\\n */\\nBaseAudioContext.prototype.createWaveShaper = function() {};\\n\\n/**\\n * @deprecated Use BaseAudioContext#createSpatialPanner or BaseAudioContext#createStereoPanner\\n * @return {!PannerNode}\\n */\\nBaseAudioContext.prototype.createPanner = function() {};\\n\\n/**\\n * @return {!StereoPannerNode}\\n */\\nBaseAudioContext.prototype.createStereoPanner = function() {};\\n\\n/**\\n * @return {!ConvolverNode}\\n */\\nBaseAudioContext.prototype.createConvolver = function() {};\\n\\n/**\\n * @param {number=} numberOfOutputs\\n * @return {!ChannelSplitterNode}\\n */\\nBaseAudioContext.prototype.createChannelSplitter = function(numberOfOutputs) {};\\n\\n/**\\n * @param {number=} numberOfInputs\\n * @return {!ChannelMergerNode}\\n */\\nBaseAudioContext.prototype.createChannelMerger = function(numberOfInputs) {};\\n\\n/**\\n * @return {!DynamicsCompressorNode}\\n */\\nBaseAudioContext.prototype.createDynamicsCompressor = function() {};\\n\\n/**\\n * @return {!OscillatorNode}\\n */\\nBaseAudioContext.prototype.createOscillator = function() {};\\n\\n/**\\n * @param {!Float32Array} real\\n * @param {!Float32Array} imag\\n * @return {!PeriodicWave}\\n */\\nBaseAudioContext.prototype.createPeriodicWave = function(real, imag) {};\\n\\n/**\\n * @return {!Promise}\\n */\\nBaseAudioContext.prototype.resume = function() {};\\n\\n/**\\n * @return {!Promise}\\n */\\nBaseAudioContext.prototype.suspend = function() {};\\n\\n/**\\n * @return {!Promise}\\n */\\nBaseAudioContext.prototype.close = function() {};\\n\\n/** @type {?function(!Event)} */\\nBaseAudioContext.prototype.onstatechange;\\n\\n/**\\n * @param {string} scriptURL\\n * @return {!Promise}\\n */\\nBaseAudioContext.prototype.createAudioWorker = function(scriptURL) {};\\n\\n/**\\n * @param {!IArrayLike} feedforward\\n * @param {!IArrayLike} feedback\\n * @return {!IIRFilterNode}\\n */\\nBaseAudioContext.prototype.createIIRFilter = function(feedforward, feedback) {};\\n\\n/**\\n * @return {!SpatialPannerNode}\\n */\\nBaseAudioContext.prototype.createSpatialPanner = function() {};\\n\\n/**\\n * @record\\n * @see https://webaudio.github.io/web-audio-api/#idl-def-AudioContextOptions\\n */\\nfunction AudioContextOptions() {};\\n\\n/** @type {(undefined|string|number)} */\\nAudioContextOptions.prototype.latencyHint;\\n\\n/** @type {(undefined|number)} */\\nAudioContextOptions.prototype.sampleRate;\\n\\n/**\\n * Includes the non-standard contextOptions optional options parameter\\n * implemented by Chrome and Firefox.\\n * @param {!AudioContextOptions=} contextOptions\\n * @constructor\\n * @extends {BaseAudioContext}\\n */\\nfunction AudioContext(contextOptions) {}\\n\\n/**\\n * @param {!HTMLMediaElement} mediaElement\\n * @return {!MediaElementAudioSourceNode}\\n */\\nAudioContext.prototype.createMediaElementSource = function(mediaElement) {};\\n\\n/**\\n * @return {!MediaStreamAudioDestinationNode}\\n */\\nAudioContext.prototype.createMediaStreamDestination = function() {};\\n\\n/**\\n * @param {!MediaStream} mediaStream\\n * @return {!MediaStreamAudioSourceNode}\\n */\\nAudioContext.prototype.createMediaStreamSource = function(mediaStream) {};\\n\\n/**\\n * @deprecated Use createScriptProcessor instead.\\n * @param {number} bufferSize\\n * @param {number} numberOfInputs\\n * @param {number} numberOfOuputs\\n * @return {!ScriptProcessorNode}\\n */\\nAudioContext.prototype.createJavaScriptNode = function(bufferSize,\\n numberOfInputs, numberOfOuputs) {};\\n\\n/**\\n * @deprecated Use createGain instead.\\n * @return {!GainNode}\\n */\\nAudioContext.prototype.createGainNode = function() {};\\n\\n/**\\n * @deprecated Use createDelay instead.\\n * @param {number=} maxDelayTime\\n * @return {!DelayNode}\\n */\\nAudioContext.prototype.createDelayNode = function(maxDelayTime) {};\\n\\n/**\\n * @param {number} numberOfChannels\\n * @param {number} length\\n * @param {number} sampleRate\\n * @constructor\\n * @extends {BaseAudioContext}\\n */\\nfunction OfflineAudioContext(numberOfChannels, length, sampleRate) {}\\n\\n/**\\n * @return {!Promise}\\n */\\nOfflineAudioContext.prototype.startRendering = function() {};\\n\\n/** @type {function(!OfflineAudioCompletionEvent)} */\\nOfflineAudioContext.prototype.oncomplete;\\n\\n/**\\n * '; +a.a+="@constructor\\n * @extends {Event}\\n */\\nfunction OfflineAudioCompletionEvent() {}\\n\\n/** @type {AudioBuffer} */\\nOfflineAudioCompletionEvent.prototype.renderedBuffer;\\n\\n/**\\n * @constructor\\n * @implements {EventTarget}\\n * @see https://www.w3.org/TR/webaudio/#the-audionode-interface\\n */\\nfunction AudioNode() {}\\n\\n/**\\n * @override\\n */\\nAudioNode.prototype.addEventListener = function(type, listener,\\n opt_useCapture) {};\\n\\n/**\\n * @override\\n */\\nAudioNode.prototype.removeEventListener = function(type, listener,\\n opt_useCapture) {};\\n\\n/**\\n * @override\\n * @return {boolean}\\n */\\nAudioNode.prototype.dispatchEvent = function(evt) {};\\n\\n/**\\n * @param {!AudioNode|!AudioParam} destination\\n * @param {number=} output\\n * @param {number=} input\\n * @return {AudioNode|void}\\n */\\nAudioNode.prototype.connect = function(destination, output, input) {};\\n\\n/**\\n * @param {!AudioNode|!AudioParam|number=} destination\\n * @param {number=} output\\n * @param {number=} input\\n */\\nAudioNode.prototype.disconnect = function(destination, output, input) {};\\n\\n/** @type {!AudioContext} */\\nAudioNode.prototype.context;\\n\\n/** @type {number} */\\nAudioNode.prototype.numberOfInputs;\\n\\n/** @type {number} */\\nAudioNode.prototype.numberOfOutputs;\\n\\n/** @type {number} */\\nAudioNode.prototype.channelCount;\\n\\n/**\\n * @type {string}\\n * See https://www.w3.org/TR/webaudio/#the-audionode-interface for valid values\\n */\\nAudioNode.prototype.channelCountMode;\\n\\n/**\\n * @type {string}\\n * See https://www.w3.org/TR/webaudio/#the-audionode-interface for valid values\\n */\\nAudioNode.prototype.channelInterpretation;\\n\\n/**\\n * @constructor\\n * @extends {AudioNode}\\n */\\nfunction AudioSourceNode() {}\\n\\n/**\\n * @constructor\\n * @extends {AudioNode}\\n */\\nfunction AudioDestinationNode() {}\\n\\n/**\\n * @deprecated Use AudioDestinationNode#maxChannelCount\\n * @type {number}\\n */\\nAudioDestinationNode.prototype.numberOfChannels;\\n\\n/** @type {number} */\\nAudioDestinationNode.prototype.maxChannelCount;\\n\\n/**\\n * @constructor\\n */\\nfunction AudioParam() {}\\n\\n/** @type {number} */\\nAudioParam.prototype.value;\\n\\n/**\\n * @deprecated\\n * @type {number}\\n */\\nAudioParam.prototype.maxValue;\\n\\n/**\\n * @deprecated\\n * @type {number}\\n */\\nAudioParam.prototype.minValue;\\n\\n/** @type {number} */\\nAudioParam.prototype.defaultValue;\\n\\n/**\\n * @deprecated\\n * @type {number}\\n */\\nAudioParam.prototype.units;\\n\\n/**\\n * @param {number} value\\n * @param {number} startTime\\n * @return {!AudioParam}\\n * @throws {!TypeError} if startTime is negative or not a finite number\\n */\\nAudioParam.prototype.setValueAtTime = function(value, startTime) {};\\n\\n/**\\n * @param {number} value\\n * @param {number} endTime\\n * @return {!AudioParam}\\n * @throws {!TypeError} if endTime is negative or not a finite number\\n */\\nAudioParam.prototype.linearRampToValueAtTime = function(value, endTime) {};\\n\\n/**\\n * @param {number} value\\n * @param {number} endTime\\n * @return {!AudioParam}\\n * @throws {!TypeError} if endTime is negative or not a finite number\\n */\\nAudioParam.prototype.exponentialRampToValueAtTime = function(value, endTime) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} startTime\\n * @param {number} timeConstant\\n * @return {!AudioParam}\\n * @throws {!TypeError} if startTime is negative or not a finite number, or\\n * timeConstant is not strictly positive\\n */\\nAudioParam.prototype.setTargetAtTime = function(target, startTime,\\n timeConstant) {};\\n\\n/**\\n * @deprecated Use setTargetAtTime instead.\\n * @param {number} target\\n * @param {number} startTime\\n * @param {number} timeConstant\\n * @return {!AudioParam}\\n */\\nAudioParam.prototype.setTargetValueAtTime = function(target, startTime,\\n timeConstant) {};\\n\\n/**\\n * @param {!Float32Array} values\\n * @param {number} startTime\\n * @param {number} duration\\n * @return {!AudioParam}\\n * @throws {!TypeError} if startTime is negative or not a finite number\\n */\\nAudioParam.prototype.setValueCurveAtTime = function(values, startTime,\\n duration) {};\\n\\n/**\\n * @param {number} startTime\\n * @return {!AudioParam}\\n * @throws {!TypeError} if startTime is negative or not a finite number\\n */\\nAudioParam.prototype.cancelScheduledValues = function(startTime) {};\\n\\n/**\\n * @constructor\\n * @extends {AudioParam}\\n */\\nfunction AudioGain() {}\\n\\n/**\\n * @constructor\\n * @extends {AudioNode}\\n */\\nfunction GainNode() {}\\n\\n/** @type {!AudioParam} */\\nGainNode.prototype.gain;\\n\\n/**\\n * @constructor\\n * @extends {AudioNode}\\n */\\nfunction DelayNode() {}\\n\\n/** @type {!AudioParam} */\\nDelayNode.prototype.delayTime;\\n\\n/**\\n * @constructor\\n */\\nfunction AudioBuffer() {}\\n\\n/**\\n * @deprecated\\n * @type {!AudioGain}\\n */\\nAudioBuffer.prototype.gain;\\n\\n/** @type {number} */\\nAudioBuffer.prototype.sampleRate;\\n\\n/** @type {number} */\\nAudioBuffer.prototype.length;\\n\\n/** @type {number} */\\nAudioBuffer.prototype.duration;\\n\\n/** @type {number} */\\nAudioBuffer.prototype.numberOfChannels;\\n\\n/**\\n * @param {number} channel\\n * @return {!Float32Array}\\n */\\nAudioBuffer.prototype.getChannelData = function(channel) {};\\n\\n/**\\n * @param {!Float32Array} destination\\n * @param {number} channelNumber\\n * @param {number=} startInChannel\\n */\\nAudioBuffer.prototype.copyFromChannel = function(destination,\\n channelNumber, startInChannel) {};\\n\\n/**\\n * @param {!Float32Array} source\\n * @param {number} channelNumber\\n * @param {number=} startInChannel\\n */\\nAudioBuffer.prototype.copyToChannel = function(source, channelNumber,\\n startInChannel) {};\\n\\n/**\\n * @constructor\\n * @extends {AudioNode}\\n */\\nfunction AudioBufferSourceNode() {}\\n\\n/**\\n * @deprecated\\n * @const {number}\\n */\\nAudioBufferSourceNode.prototype.UNSCHEDULED_STATE;\\n\\n/**\\n * @deprecated\\n * @const {number}\\n */\\nAudioBufferSourceNode.prototype.SCHEDULED_STATE;\\n\\n/**\\n * @deprecated\\n * @const {number}\\n */\\nAudioBufferSourceNode.prototype.PLAYING_STATE;\\n\\n/**\\n * @deprecated\\n * @const {number}\\n */\\nAudioBufferSourceNode.prototype.FINISHED_STATE;\\n\\n/**\\n * @deprecated\\n * @type {number}\\n */\\nAudioBufferSourceNode.prototype.playbackState;\\n\\n/** @type {AudioBuffer} */\\nAudioBufferSourceNode.prototype.buffer;\\n\\n/**\\n * @deprecated\\n * @type {number}\\n */\\nAudioBufferSourceNode.prototype.gain;\\n\\n/** @type {!AudioParam} */\\nAudioBufferSourceNode.prototype.playbackRate;\\n\\n/** @type {boolean} */\\nAudioBufferSourceNode.prototype.loop;\\n\\n/** @type {number} */\\nAudioBufferSourceNode.prototype.loopStart;\\n\\n/** @type {number} */\\nAudioBufferSourceNode.prototype.loopEnd;\\n\\n/** @type {?function(!Event): void} */\\nAudioBufferSourceNode.prototype.onended;\\n\\n/** @type {!AudioParam} */\\nAudioBufferSourceNode.prototype.detune;\\n\\n/**\\n * @param {number=} when\\n * @param {number=} opt_offset\\n * @param {number=} opt_duration\\n * @throws {!TypeError} if any parameter is negative\\n */\\nAudioBufferSourceNode.prototype.start = function(when, opt_offset,\\n opt_duration) {};\\n\\n/**\\n * @param {number=} when\\n * @throws {!TypeError} if when is negative\\n */\\nAudioBufferSourceNode.prototype.stop = function(when) {};\\n\\n/**\\n * @deprecated Use AudioBufferSourceNode#start\\n * @param {number} when\\n * @return {undefined}\\n */\\nAudioBufferSourceNode.prototype.noteOn = function(when) {};\\n\\n/**\\n * @param {number=} when\\n * @param {number=} opt_offset\\n * @param {number=} opt_duration\\n * @deprecated Use AudioBufferSourceNode#start\\n */\\nAudioBufferSourceNode.prototype.noteGrainOn = function(when, opt_offset,\\n opt_duration) {};\\n\\n/**\\n * @param {number} when\\n * @deprecated Use AudioBufferSourceNode#stop\\n */\\nAudioBufferSourceNode.prototype.noteOff = function(when) {};\\n\\n/**\\n * @constructor\\n * @extends {AudioNode}\\n */\\nfunction MediaElementAudioSourceNode() {}\\n\\n/**\\n * @constructor\\n */\\nfunction AudioWorker() {}\\n\\n/** @type {?function(!Event)} */\\nAudioWorker.prototype.onloaded;\\n\\n/** @type {?function(!Event)} */\\nAudioWorker.prototype.onmessage;\\n\\n/** @type {!Array} */\\nAudioWorker.prototype.parameters;\\n\\n/**\\n * @param {string} name\\n * @param {number} defaultValue\\n * @return {!AudioParam}\\n */\\nAudioWorker.prototype.addParameter = function(name, defaultValue) {};\\n\\n/**\\n * @param {number} numberOfInputs\\n * @param {number} numberOfOutputs\\n * @return {!AudioWorkerNode}\\n */\\nAudioWorker.prototype.createNode = function(numberOfInputs, numberOfOutputs) {};\\n\\n/**\\n * @param {*} message\\n * @param {!Array=} transfer\\n */\\nAudioWorker.prototype.postMessage = function(message, transfer) {};\\n\\n/**\\n * @param {string} name\\n */\\nAudioWorker.prototype.removeParameter = function(name) {};\\n\\n/**\\n */\\nAudioWorker.prototype.terminate = function() {};\\n\\n/**\\n * @constructor\\n * @extends {AudioNode}\\n */\\nfunction AudioWorkerNode() {}\\n\\n/** @type {?function(!Event)} */\\nAudioWorkerNode.prototype.onmessage;\\n\\n/**\\n * @param {*} message\\n * @param {!Array=} transfer\\n */\\nAudioWorkerNode.prototype.postMessage = function(message, transfer) {};\\n\\n/**\\n * @constructor\\n */\\nfunction AudioWorkerParamDescriptor() {}\\n\\n/** @type {number} */\\nAudioWorkerParamDescriptor.prototype.defaultValue;\\n\\n/** @type {string} */\\nAudioWorkerParamDescriptor.prototype.name;\\n\\n/**\\n * @constructor\\n */\\nfunction AudioWorkerGlobalScope() {}\\n\\n/** @type {?function(!Event)} */\\nAudioWorkerGlobalScope.prototype.onaudioprocess;\\n\\n/** @type {?function(!Event)} */\\nAudioWorkerGlobalScope.prototype.onnodecreate;\\n\\n/** @type {!Array} */\\nAudioWorkerGlobalScope.prototype.parameters;\\n\\n/** @type {number} */\\nAudioWorkerGlobalScope.prototype.sampleRate;\\n\\n/**\\n * @param {string} name\\n * @param {number} defaultValue\\n * @return {!AudioParam}\\n */\\nAudioWorkerGlobalScope.prototype.addParameter = function(name, defaultValue) {};\\n\\n/**\\n * @param {string} name\\n */\\nAudioWorkerGlobalScope.prototype.removeParameter = function(name) {};\\n\\n/**\\n * @constructor\\n */\\nfunction AudioWorkerNodeProcessor() {}\\n\\n/** @type {?function(!Event)} */\\nAudioWorkerNodeProcessor.prototype.onmessage;\\n\\n/**\\n * @param {*} message\\n * @param {!Array=} transfer\\n */\\nAudioWorkerNodeProcessor.prototype.postMessage = function(message, transfer) {};\\n\\n/**\\n * @constructor\\n * @extends {AudioNode}\\n * @deprecated Use AudioWorkerNode\\n */\\nfunction JavaScriptAudioNode() {}\\n\\n/**\\n * @type {EventListener|(function(!AudioProcessingEvent):(boolean|undefined))}\\n * @deprecated Use AudioWorkerNode\\n */\\nJavaScriptAudioNode.prototype.onaudioprocess;\\n\\n/**\\n * @type {number}\\n * @deprecated Use AudioWorkerNode\\n */\\nJavaScriptAudioNode.prototype.bufferSize;\\n\\n/**\\n * @constructor\\n * @extends {AudioNode}\\n * @deprecated Use AudioWorkerNode\\n */\\nfunction ScriptProcessorNode() {}\\n\\n/**\\n * @type {EventListener|(function(!AudioProcessingEvent):(boolean|undefined))}\\n * @deprecated Use AudioWorkerNode\\n */\\nScriptProcessorNode.prototype.onaudioprocess;\\n\\n/**\\n * @type {number}\\n * @deprecated Use AudioWorkerNode\\n */\\nScriptProcessorNode.prototype.bufferSize;\\n\\n/**\\n * @constructor\\n * @extends {Event}\\n */\\nfunction AudioWorkerNodeCreationEvent() {}\\n\\n/** @type {!Array} */\\nAudioWorkerNodeCreationEvent.prototype.inputs;\\n\\n/** @type {!AudioWorkerNodeProcessor} */\\nAudioWorkerNodeCreationEvent.prototype.node;\\n\\n/** @type {!Array} */\\nAudioWorkerNodeCreationEvent.prototype.outputs;\\n\\n/**\\n * @constructor\\n * @extends {Event}\\n */\\nfunction AudioProcessEvent() {}\\n\\n/** @type {!Float32Array} */\\nAudioProcessEvent.prototype.inputs;\\n\\n/** @type {!AudioWorkerNodeProcessor} */\\nAudioProcessEvent.prototype.node;\\n\\n/** @type {!Float32Array} */\\nAudioProcessEvent.prototype.outputs;\\n\\n/** @type {!Object} */\\nAudioProcessEvent.prototype.parameters;\\n\\n/** @type {number} */\\nAudioProcessEvent.prototype.playbackTime;\\n\\n/**\\n * @constructor\\n * @extends {Event}\\n * @deprecated Use AudioProcessEvent\\n */\\nfunction AudioProcessingEvent() {}\\n\\n/**\\n * @type {!ScriptProcessorNode}\\n * @deprecated Use AudioProcessEvent\\n */\\nAudioProcessingEvent.prototype.node;\\n\\n/**\\n * @type {number}\\n * @deprecated Use AudioProcessEvent\\n */\\nAudioProcessingEvent.prototype.playbackTime;\\n\\n/**\\n * @type {!AudioBuffer}\\n * @deprecated Use AudioProcessEvent\\n */\\nAudioProcessingEvent.prototype.inputBuffer;\\n\\n/**\\n * @type {!AudioBuffer}\\n * @deprecated Use AudioProcessEvent\\n */\\nAudioProcessingEvent.prototype.outputBuffer;\\n\\n/**\\n * @deprecated\\n * @constructor\\n * @extends {AudioNode}\\n */\\nfunction AudioPannerNode() {}\\n\\n/**\\n * @deprecated\\n * @const {number}\\n */\\nAudioPannerNode.prototype.EQUALPOWER;\\n\\n/**\\n * @deprecated\\n * @const {number}\\n */\\nAudioPannerNode.prototype.HRTF;\\n\\n/**\\n * @deprecated\\n * @const {number}\\n */\\nAudioPannerNode.prototype.SOUNDFIELD;\\n\\n/**\\n * @deprecated\\n * @const {number}\\n */\\nAudioPannerNode.prototype.LINEAR_DISTANCE;\\n\\n/**\\n * @deprecated\\n * @const {number}\\n */\\nAudioPannerNode.prototype.INVERSE_DISTANCE;\\n\\n/**\\n * @deprecated\\n * @const {number}\\n */\\nAudioPannerNode.prototype.EXPONENTIAL_DISTANCE;\\n\\n/**\\n * @deprecated\\n * @type {number|string}\\n */\\nAudioPannerNode.prototype.panningModel;\\n\\n/**\\n * @deprecated\\n * @param {number} x\\n * @param {number} y\\n * @param {number} z\\n * @return {undefined}\\n */\\nAudioPannerNode.prototype.setPosition = function(x, y, z) {};\\n\\n/**\\n * @deprecated\\n * @param {number} x\\n * @param {number} y\\n * @param {number} z\\n * @return {undefined}\\n */\\nAudioPannerNode.prototype.setOrientation = function(x, y, z) {};\\n\\n/**\\n * @deprecated\\n * @param {number} x\\n * @param {number} y\\n * @param {number} z\\n * @return {undefined}\\n */\\nAudioPannerNode.prototype.setVelocity = function(x, y, z) {};\\n\\n/**\\n * @deprecated\\n * @type {number|string}\\n */\\nAudioPannerNode.prototype.distanceModel;\\n\\n/**\\n * @deprecated\\n * @type {number}\\n */\\nAudioPannerNode.prototype.refDistance;\\n\\n/**\\n * @deprecated\\n * @type {number}\\n */\\nAudioPannerNode.prototype.maxDistance;\\n\\n/**\\n * @deprecated\\n * @type {number}\\n */\\nAudioPannerNode.prototype.rolloffFactor;\\n\\n/**\\n * @deprecated\\n * @type {number}\\n */\\nAudioPannerNode.prototype.coneInnerAngle;\\n\\n/**\\n * @deprecated\\n * @type {number}\\n */\\nAudioPannerNode.prototype.coneOuterAngle;\\n\\n/**\\n * @deprecated\\n * @type {number}\\n */\\nAudioPannerNode.prototype.coneOuterGain;\\n\\n/**\\n * @deprecated\\n * @type {!AudioGain}\\n */\\nAudioPannerNode.prototype.coneGain;\\n\\n/**\\n * @deprecated\\n * @type {!AudioGain}\\n */\\nAudioPannerNode.prototype.distanceGain;\\n\\n/**\\n * @constructor\\n * @extends {AudioNode}\\n */\\nfunction PannerNode() {}\\n\\n/** @type {number} */\\nPannerNode.prototype.coneInnerAngle;\\n\\n/** @type {number} */\\nPannerNode.prototype.coneOuterAngle;\\n\\n/** @type {number} */\\nPannerNode.prototype.coneOuterGain;\\n\\n/**\\n * @type {string}\\n * See https://www.w3.org/TR/webaudio/#the-pannernode-interface for valid values\\n */\\nPannerNode.prototype.distanceModel;\\n\\n/** @type {number} */\\nPannerNode.prototype.maxDistance;\\n\\n/**\\n * @type {string}\\n * See https://www.w3.org/TR/webaudio/#the-pannernode-interface for valid values\\n */\\nPannerNode.prototype.panningModel;\\n\\n/** @type {number} */\\nPannerNode.prototype.refDistance;\\n\\n/** @type {number} */\\nPannerNode.prototype.rolloffFactor;\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @param {number} z\\n */\\nPannerNode.prototype.setOrientation = function(x, y, z) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @param {number} z\\n */\\nPannerNode.prototype.setPosition = function(x, y, z) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @param {number} z\\n */\\nPannerNode.prototype.setVelocity = function(x, y, z) {};\\n\\n/**\\n * @constructor\\n * @deprecated Use SpatialListener\\n */\\nfunction AudioListener() {}\\n\\n/**\\n * @type {number}\\n * @deprecated Use SpatialListener\\n */\\nAudioListener.prototype.gain;\\n\\n/**\\n * @type {number}\\n * @deprecated Use SpatialListener\\n */\\nAudioListener.prototype.dopplerFactor;\\n\\n/**\\n * @type {number}\\n * @deprecated Use SpatialListener\\n */\\nAudioListener.prototype.speedOfSound;\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @param {number} z\\n * @deprecated Use SpatialListener\\n */\\nAudioListener.prototype.setPosition = function(x, y, z) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @param {number} z\\n * @param {number"; +a.a+='} xUp\\n * @param {number} yUp\\n * @param {number} zUp\\n * @deprecated Use SpatialListener\\n */\\nAudioListener.prototype.setOrientation = function(x, y, z, xUp, yUp, zUp) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @param {number} z\\n * @deprecated Use SpatialListener\\n */\\nAudioListener.prototype.setVelocity = function(x, y, z) {};\\n\\n/**\\n * @constructor\\n * @extends {AudioNode}\\n */\\nfunction SpatialPannerNode() {}\\n\\n/** @type {number} */\\nSpatialPannerNode.prototype.coneInnerAngle;\\n\\n/** @type {number} */\\nSpatialPannerNode.prototype.coneOuterAngle;\\n\\n/** @type {number} */\\nSpatialPannerNode.prototype.coneOuterGain;\\n\\n/**\\n * @type {string}\\n * See https://www.w3.org/TR/webaudio/#the-pannernode-interface for valid values\\n */\\nSpatialPannerNode.prototype.distanceModel;\\n\\n/** @type {number} */\\nSpatialPannerNode.prototype.maxDistance;\\n\\n/** @type {!AudioParam} */\\nSpatialPannerNode.prototype.orientationX;\\n\\n/** @type {!AudioParam} */\\nSpatialPannerNode.prototype.orientationY;\\n\\n/** @type {!AudioParam} */\\nSpatialPannerNode.prototype.orientationZ;\\n\\n/**\\n * @type {string}\\n * See https://www.w3.org/TR/webaudio/#the-pannernode-interface for valid values\\n */\\nSpatialPannerNode.prototype.panningModel;\\n\\n/** @type {!AudioParam} */\\nSpatialPannerNode.prototype.positionX;\\n\\n/** @type {!AudioParam} */\\nSpatialPannerNode.prototype.positionY;\\n\\n/** @type {!AudioParam} */\\nSpatialPannerNode.prototype.positionZ;\\n\\n/** @type {number} */\\nSpatialPannerNode.prototype.refDistance;\\n\\n/** @type {number} */\\nSpatialPannerNode.prototype.rolloffFactor;\\n\\n/**\\n * @constructor\\n */\\nfunction SpatialListener() {}\\n\\n/** @type {!AudioParam} */\\nSpatialListener.prototype.forwardX;\\n\\n/** @type {!AudioParam} */\\nSpatialListener.prototype.forwardY;\\n\\n/** @type {!AudioParam} */\\nSpatialListener.prototype.forwardZ;\\n\\n/** @type {!AudioParam} */\\nSpatialListener.prototype.positionX;\\n\\n/** @type {!AudioParam} */\\nSpatialListener.prototype.positionY;\\n\\n/** @type {!AudioParam} */\\nSpatialListener.prototype.positionZ;\\n\\n/** @type {!AudioParam} */\\nSpatialListener.prototype.upX;\\n\\n/** @type {!AudioParam} */\\nSpatialListener.prototype.upY;\\n\\n/** @type {!AudioParam} */\\nSpatialListener.prototype.upZ;\\n\\n/**\\n * @constructor\\n * @extends {AudioNode}\\n * @see http://webaudio.github.io/web-audio-api/#the-stereopannernode-interface\\n */\\nfunction StereoPannerNode() {}\\n\\n/** @type {!AudioParam} */\\nStereoPannerNode.prototype.pan;\\n\\n/**\\n * @constructor\\n * @extends {AudioNode}\\n */\\nfunction ConvolverNode() {}\\n\\n/** @type {?AudioBuffer} */\\nConvolverNode.prototype.buffer;\\n\\n/** @type {boolean} */\\nConvolverNode.prototype.normalize;\\n\\n/**\\n * @constructor\\n * @extends {AudioNode}\\n */\\nvar AnalyserNode = function() {};\\n\\n/**\\n * @param {!Float32Array} array\\n */\\nAnalyserNode.prototype.getFloatFrequencyData = function(array) {};\\n\\n/**\\n * @param {!Uint8Array} array\\n */\\nAnalyserNode.prototype.getByteFrequencyData = function(array) {};\\n\\n/**\\n * @param {!Uint8Array} array\\n */\\nAnalyserNode.prototype.getByteTimeDomainData = function(array) {};\\n\\n/**\\n * @param {!Float32Array} array\\n */\\nAnalyserNode.prototype.getFloatTimeDomainData = function(array) {};\\n\\n/** @type {number} */\\nAnalyserNode.prototype.fftSize;\\n\\n/** @type {number} */\\nAnalyserNode.prototype.frequencyBinCount;\\n\\n/** @type {number} */\\nAnalyserNode.prototype.minDecibels;\\n\\n/** @type {number} */\\nAnalyserNode.prototype.maxDecibels;\\n\\n/** @type {number} */\\nAnalyserNode.prototype.smoothingTimeConstant;\\n\\n/**\\n * @constructor\\n * @extends {AnalyserNode}\\n * @deprecated Use AnalyserNode\\n *\\n * This constructor has been added for backwards compatibility.\\n */\\nvar RealtimeAnalyserNode = function() {};\\n\\n/**\\n * @constructor\\n * @extends {AudioNode}\\n */\\nfunction ChannelSplitterNode() {}\\n\\n/**\\n * @constructor\\n * @extends {ChannelSplitterNode}\\n * @deprecated Use ChannelSplitterNode\\n *\\n * This constructor has been added for backwards compatibility.\\n */\\nfunction AudioChannelSplitter() {}\\n\\n/**\\n * @constructor\\n * @extends {AudioNode}\\n */\\nfunction ChannelMergerNode() {}\\n\\n/**\\n * @constructor\\n * @extends {ChannelMergerNode}\\n * @deprecated Use ChannelMergerNode\\n *\\n * This constructor has been added for backwards compatibility.\\n */\\nfunction AudioChannelMerger() {}\\n\\n/**\\n * @constructor\\n * @extends {AudioNode}\\n */\\nfunction DynamicsCompressorNode() {}\\n\\n/** @type {!AudioParam} */\\nDynamicsCompressorNode.prototype.threshold;\\n\\n/** @type {!AudioParam} */\\nDynamicsCompressorNode.prototype.knee;\\n\\n/** @type {!AudioParam} */\\nDynamicsCompressorNode.prototype.ratio;\\n\\n/** @type {number} */\\nDynamicsCompressorNode.prototype.reduction;\\n\\n/** @type {!AudioParam} */\\nDynamicsCompressorNode.prototype.attack;\\n\\n/** @type {!AudioParam} */\\nDynamicsCompressorNode.prototype.release;\\n\\n/**\\n * @constructor\\n * @extends {AudioNode}\\n */\\nfunction BiquadFilterNode() {}\\n\\n/**\\n * A read-able and write-able string that specifies the type of the filter.\\n * See http://webaudio.github.io/web-audio-api/#the-biquadfilternode-interface\\n * for valid values.\\n * @type {string}\\n */\\nBiquadFilterNode.prototype.type;\\n\\n/** @type {!AudioParam} */\\nBiquadFilterNode.prototype.frequency;\\n\\n/** @type {!AudioParam} */\\nBiquadFilterNode.prototype.detune;\\n\\n/** @type {!AudioParam} */\\nBiquadFilterNode.prototype.Q;\\n\\n/** @type {!AudioParam} */\\nBiquadFilterNode.prototype.gain;\\n/**\\n * @param {Float32Array} frequencyHz\\n * @param {Float32Array} magResponse\\n * @param {Float32Array} phaseResponse\\n * @return {undefined}\\n */\\nBiquadFilterNode.prototype.getFrequencyResponse = function(\\n frequencyHz, magResponse, phaseResponse) {};\\n\\n/**\\n * @constructor\\n * @extends {AudioNode}\\n */\\nfunction IIRFilterNode() {}\\n\\n/**\\n * @param {!Float32Array} frequencyHz\\n * @param {!Float32Array} magResponse\\n * @param {!Float32Array} phaseResponse\\n * @return {undefined}\\n */\\nIIRFilterNode.prototype.getFrequencyResponse = function(\\n frequencyHz, magResponse, phaseResponse) {};\\n\\n/**\\n * @constructor\\n * @extends {AudioNode}\\n */\\nfunction WaveShaperNode() {}\\n\\n/** @type {Float32Array} */\\nWaveShaperNode.prototype.curve;\\n\\n/** @type {string} */\\nWaveShaperNode.prototype.oversample;\\n\\n/**\\n * @deprecated\\n * @constructor\\n */\\nfunction WaveTable() {}\\n\\n/**\\n * @constructor\\n * @extends {AudioNode}\\n */\\nfunction OscillatorNode() {}\\n\\n/**\\n * @type {string}\\n * See https://www.w3.org/TR/webaudio/#the-oscillatornode-interface for valid values\\n */\\nOscillatorNode.prototype.type;\\n\\n/**\\n * @deprecated\\n * @type {number}\\n */\\nOscillatorNode.prototype.playbackState;\\n\\n/** @type {!AudioParam} */\\nOscillatorNode.prototype.frequency;\\n\\n/** @type {!AudioParam} */\\nOscillatorNode.prototype.detune;\\n\\n/**\\n * @param {number=} when\\n */\\nOscillatorNode.prototype.start = function(when) {};\\n\\n/**\\n * @param {number=} when\\n */\\nOscillatorNode.prototype.stop = function(when) {};\\n\\n/**\\n * @deprecated\\n * @param {!WaveTable} waveTable\\n */\\nOscillatorNode.prototype.setWaveTable = function(waveTable) {};\\n\\n/**\\n * @param {!PeriodicWave} periodicWave\\n */\\nOscillatorNode.prototype.setPeriodicWave = function(periodicWave) {};\\n\\n/** @type {?function(!Event)} */\\nOscillatorNode.prototype.onended;\\n\\n/**\\n * @constructor\\n */\\nfunction PeriodicWave() {}\\n\\n/**\\n * @constructor\\n * @extends {AudioNode}\\n */\\nfunction MediaStreamAudioSourceNode() {}\\n\\n/**\\n * @constructor\\n * @extends {AudioNode}\\n */\\nfunction MediaStreamAudioDestinationNode() {}\\n\\n/** @type {!MediaStream} */\\nMediaStreamAudioDestinationNode.prototype.stream;\\n\\n/**\\n * Definitions for the Web Audio API with webkit prefix.\\n */\\n\\n/**\\n * @constructor\\n * @extends {AudioContext}\\n */\\nfunction webkitAudioContext() {}\\n\\n/**\\n * @param {number} numberOfChannels\\n * @param {number} length\\n * @param {number} sampleRate\\n * @constructor\\n * @extends {OfflineAudioContext}\\n */\\nfunction webkitOfflineAudioContext(numberOfChannels, length, sampleRate) {}\\n\\n/**\\n * @constructor\\n * @extends {AudioPannerNode}\\n */\\nfunction webkitAudioPannerNode() {}\\n\\n/**\\n * @constructor\\n * @extends {PannerNode}\\n */\\nfunction webkitPannerNode() {}\\n\\n/**\\n * Definitions for the Audio API as implemented in Firefox.\\n * Please note that this document describes a non-standard experimental API.\\n * This API is considered deprecated.\\n * @see https://developer.mozilla.org/en/DOM/HTMLAudioElement\\n */\\n\\n/**\\n * @param {string=} src\\n * @constructor\\n * @extends {HTMLAudioElement}\\n */\\nfunction Audio(src) {}\\n\\n/**\\n * @param {number} channels\\n * @param {number} rate\\n */\\nAudio.prototype.mozSetup = function(channels, rate) {};\\n\\n/**\\n * @param {Array|Float32Array} buffer\\n */\\nAudio.prototype.mozWriteAudio = function(buffer) {};\\n\\n/**\\n * @return {number}\\n */\\nAudio.prototype.mozCurrentSampleOffset = function() {};\\n","externs/w3c_batterystatus.js":"/*\\n * Copyright 2015 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for W3C\'s Battery Status API.\\n * The whole file has been fully type annotated. Created from\\n * http://www.w3.org/TR/2014/CR-battery-status-20141209/\\n *\\n * @externs\\n */\\n\\n\\n\\n/**\\n * @interface\\n * @extends {EventTarget}\\n */\\nfunction BatteryManager() {}\\n\\n\\n/**\\n * @type {boolean}\\n */\\nBatteryManager.prototype.charging;\\n\\n\\n/**\\n * @type {number}\\n */\\nBatteryManager.prototype.chargingTime;\\n\\n\\n/**\\n * @type {number}\\n */\\nBatteryManager.prototype.dischargingTime;\\n\\n\\n/**\\n * @type {number}\\n */\\nBatteryManager.prototype.level;\\n\\n\\n/**\\n * @type {?function(!Event)}\\n */\\nBatteryManager.prototype.onchargingchange;\\n\\n\\n/**\\n * @type {?function(!Event)}\\n */\\nBatteryManager.prototype.onchargingtimechange;\\n\\n\\n/**\\n * @type {?function(!Event)}\\n */\\nBatteryManager.prototype.ondischargingtimechange;\\n\\n\\n/**\\n * @type {?function(!Event)}\\n */\\nBatteryManager.prototype.onlevelchange;\\n","externs/w3c_clipboard.js":"/*\\n * Copyright 2018 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Declaration of the asynchronous clipboard Web API.\\n * @externs\\n */\\n\\n/**\\n * @interface\\n * @see https://w3c.github.io/clipboard-apis/#async-clipboard-api\\n */\\nfunction Clipboard() {}\\n\\n/**\\n * @return {!Promise}\\n */\\nClipboard.prototype.readText = function() {};\\n\\n/**\\n * @param {string} text\\n * @return {!Promise}\\n */\\nClipboard.prototype.writeText = function(text) {};\\n\\n/** @const {!Clipboard} */\\nNavigator.prototype.clipboard;\\n","externs/w3c_composition_event.js":"/*\\n * Copyright 2018 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for W3C\'s Composition Events specification.\\n * @externs\\n */\\n\\n/**\\n * The `CompositionEvent` interface provides specific contextual information\\n * associated with Composition Events.\\n * @see https://www.w3.org/TR/uievents/#interface-compositionevent\\n * @record\\n * @extends {UIEventInit}\\n */\\nfunction CompositionEventInit() {}\\n\\n/**\\n * `data` holds the value of the characters generated by an input method. This\\n * MAY be a single Unicode character or a non-empty sequence of Unicode\\n * characters. This attribute MAY be the empty string. The un-initialized value\\n * of this attribute MUST be \\"\\" (the empty string).\\n * @type {string}\\n */\\nCompositionEventInit.prototype.data;\\n\\n/**\\n * Composition Events provide a means for inputing text in a supplementary or\\n * alternate manner than by Keyboard Events, in order to allow the use of\\n * characters that might not be commonly available on keyboard. For example,\\n * Composition Events might be used to add accents to characters despite their\\n * absence from standard US keyboards, to build up logograms of many Asian\\n * languages from their base components or categories, to select word choices\\n * from a combination of key presses on a mobile device keyboard, or to convert\\n * voice commands into text using a speech recognition processor.\\n *\\n * Conceptually, a composition session consists of one `compositionstart` event,\\n * one or more `compositionupdate` events, and one `compositionend` event, with\\n * the value of the data attribute persisting between each stage of this event\\n * chain during each session.\\n *\\n * Not all IME systems or devices expose the necessary data to the DOM, so the\\n * active composition string (the \\"Reading Window\\" or \\"candidate selection\\" menu\\n * option) might not be available through this interface, in which case the\\n * selection MAY be represented by the empty string.\\n *\\n * @see https://www.w3.org/TR/uievents/#events-compositionevents\\n * @param {string} type\\n * @param {!CompositionEventInit=} opt_eventInitDict\\n * @extends {UIEvent}\\n * @constructor\\n */\\nfunction CompositionEvent(type, opt_eventInitDict) {}\\n\\n/**\\n * Initializes attributes of a `CompositionEvent` object. This method has the\\n * same behavior as `UIEvent.initUIEvent()`. The value of `detail` remains\\n * undefined.\\n *\\n * @see https://www.w3.org/TR/uievents/#idl-interface-CompositionEvent-initializers\\n * @param {string} typeArg\\n * @param {boolean} canBubbleArg\\n * @param {boolean} cancelableArg\\n * @param {?Window} viewArg\\n * @param {string} dataArg\\n * @param {string} localeArg\\n * @return {undefined}\\n */\\nCompositionEvent.prototype.initCompositionEvent = function(\\n typeArg, canBubbleArg, cancelableArg, viewArg, dataArg, localeArg) {};\\n\\n/**\\n * @type {string}\\n */\\nCompositionEvent.prototype.data;\\n\\n/**\\n * @type {string}\\n */\\nCompositionEvent.prototype.locale;\\n","externs/w3c_css3d.js":"/*\\n * Copyright 2010 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for W3C\'s CSS 3D Transforms specification.\\n * The whole file has been fully type annotated. Created from\\n * https://dvcs.w3.org/hg/FXTF/raw-file/tip/matrix/index.html\\n *\\n * @externs\\n * @author rjfioravanti@google.com (Ryan Fioravanti)\\n */\\n\\n/**\\n * @constructor\\n * @param {string=} opt_matrix\\n * @see https://dvcs.w3.org/hg/FXTF/raw-file/tip/matrix/index.html#the-cssmatrix-interface\\n */\\nfunction CSSMatrix(opt_matrix) {}\\n\\n/**\\n * @typ'; +a.a+='e {number}\\n * @see https://dvcs.w3.org/hg/FXTF/raw-file/tip/matrix/index.html#three-dimensional-attributes\\n */\\nCSSMatrix.prototype.m11;\\n\\n/**\\n * @type {number}\\n * @see https://dvcs.w3.org/hg/FXTF/raw-file/tip/matrix/index.html#three-dimensional-attributes\\n */\\nCSSMatrix.prototype.m12;\\n\\n/**\\n * @type {number}\\n * @see https://dvcs.w3.org/hg/FXTF/raw-file/tip/matrix/index.html#three-dimensional-attributes\\n */\\nCSSMatrix.prototype.m13;\\n\\n/**\\n * @type {number}\\n * @see https://dvcs.w3.org/hg/FXTF/raw-file/tip/matrix/index.html#three-dimensional-attributes\\n */\\nCSSMatrix.prototype.m14;\\n\\n/**\\n * @type {number}\\n * @see https://dvcs.w3.org/hg/FXTF/raw-file/tip/matrix/index.html#three-dimensional-attributes\\n */\\nCSSMatrix.prototype.m21;\\n\\n/**\\n * @type {number}\\n * @see https://dvcs.w3.org/hg/FXTF/raw-file/tip/matrix/index.html#three-dimensional-attributes\\n */\\nCSSMatrix.prototype.m22;\\n\\n/**\\n * @type {number}\\n * @see https://dvcs.w3.org/hg/FXTF/raw-file/tip/matrix/index.html#three-dimensional-attributes\\n */\\nCSSMatrix.prototype.m23;\\n\\n/**\\n * @type {number}\\n * @see https://dvcs.w3.org/hg/FXTF/raw-file/tip/matrix/index.html#three-dimensional-attributes\\n */\\nCSSMatrix.prototype.m24;\\n\\n/**\\n * @type {number}\\n * @see https://dvcs.w3.org/hg/FXTF/raw-file/tip/matrix/index.html#three-dimensional-attributes\\n */\\nCSSMatrix.prototype.m31;\\n\\n/**\\n * @type {number}\\n * @see https://dvcs.w3.org/hg/FXTF/raw-file/tip/matrix/index.html#three-dimensional-attributes\\n */\\nCSSMatrix.prototype.m32;\\n\\n/**\\n * @type {number}\\n * @see https://dvcs.w3.org/hg/FXTF/raw-file/tip/matrix/index.html#three-dimensional-attributes\\n */\\nCSSMatrix.prototype.m33;\\n\\n/**\\n * @type {number}\\n * @see https://dvcs.w3.org/hg/FXTF/raw-file/tip/matrix/index.html#three-dimensional-attributes\\n */\\nCSSMatrix.prototype.m34;\\n\\n/**\\n * @type {number}\\n * @see https://dvcs.w3.org/hg/FXTF/raw-file/tip/matrix/index.html#three-dimensional-attributes\\n */\\nCSSMatrix.prototype.m41;\\n\\n/**\\n * @type {number}\\n * @see https://dvcs.w3.org/hg/FXTF/raw-file/tip/matrix/index.html#three-dimensional-attributes\\n */\\nCSSMatrix.prototype.m42;\\n\\n/**\\n * @type {number}\\n * @see https://dvcs.w3.org/hg/FXTF/raw-file/tip/matrix/index.html#three-dimensional-attributes\\n */\\nCSSMatrix.prototype.m43;\\n\\n/**\\n * @type {number}\\n * @see https://dvcs.w3.org/hg/FXTF/raw-file/tip/matrix/index.html#three-dimensional-attributes\\n */\\nCSSMatrix.prototype.m44;\\n\\n/**\\n * @param {string} string\\n * @return {void}\\n */\\nCSSMatrix.prototype.setMatrixValue = function(string) {};\\n\\n/**\\n * @param {!CSSMatrix} secondMatrix\\n * @return {!CSSMatrix}\\n * @see https://dvcs.w3.org/hg/FXTF/raw-file/tip/matrix/index.html#widl-CSSMatrix-multiply-CSSMatrix-CSSMatrix-other\\n */\\nCSSMatrix.prototype.multiply = function(secondMatrix) {};\\n\\n/**\\n * @return {CSSMatrix} Returns void if the matrix is non-invertable.\\n * @see https://dvcs.w3.org/hg/FXTF/raw-file/tip/matrix/index.html#widl-CSSMatrix-inverse-CSSMatrix\\n */\\nCSSMatrix.prototype.inverse = function() {};\\n\\n/**\\n * @param {number=} opt_x Defaults to 0.\\n * @param {number=} opt_y Defaults to 0.\\n * @param {number=} opt_z Defaults to 0.\\n * @return {!CSSMatrix}\\n * @see https://dvcs.w3.org/hg/FXTF/raw-file/tip/matrix/index.html#widl-CSSMatrix-translate-CSSMatrix-unrestricted-double-tx-unrestricted-double-ty-unrestricted-double-tz\\n */\\nCSSMatrix.prototype.translate = function(opt_x, opt_y, opt_z) {};\\n\\n/**\\n * @param {number=} opt_scaleX Defaults to 1.\\n * @param {number=} opt_scaleY Defaults to scaleX.\\n * @param {number=} opt_scaleZ Defaults to 1.\\n * @return {!CSSMatrix}\\n * @see https://dvcs.w3.org/hg/FXTF/raw-file/tip/matrix/index.html#widl-CSSMatrix-scale-CSSMatrix-unrestricted-double-scale-unrestricted-double-originX-unrestricted-double-originY\\n */\\nCSSMatrix.prototype.scale = function(opt_scaleX, opt_scaleY, opt_scaleZ) {};\\n\\n/**\\n * @param {number=} opt_rotX Defaults to 0.\\n * @param {number=} opt_rotY Defaults to 0.\\n * @param {number=} opt_rotZ Defaults to rotX if rotY is not defined, else 0.\\n * @return {!CSSMatrix}\\n * @see https://dvcs.w3.org/hg/FXTF/raw-file/tip/matrix/index.html#widl-CSSMatrix-rotate-CSSMatrix-unrestricted-double-angle-unrestricted-double-originX-unrestricted-double-originY\\n */\\nCSSMatrix.prototype.rotate = function(opt_rotX, opt_rotY, opt_rotZ) {};\\n\\n/**\\n * @param {number=} opt_x Defaults to 0.\\n * @param {number=} opt_y Defaults to 0.\\n * @param {number=} opt_z Defaults to 0.\\n * @param {number=} opt_angle Defaults to 0.\\n * @return {!CSSMatrix}\\n * @see https://dvcs.w3.org/hg/FXTF/raw-file/tip/matrix/index.html#widl-CSSMatrix-rotateAxisAngle-CSSMatrix-unrestricted-double-x-unrestricted-double-y-unrestricted-double-z-unrestricted-double-angle\\n */\\nCSSMatrix.prototype.rotateAxisAngle =\\n function(opt_x, opt_y, opt_z, opt_angle) {};\\n\\n/**\\n * @constructor\\n * @param {string=} opt_matrix\\n * @extends {CSSMatrix}\\n * @see http://developer.apple.com/safari/library/documentation/AudioVideo/Reference/WebKitCSSMatrixClassReference/WebKitCSSMatrix/WebKitCSSMatrix.html#//apple_ref/javascript/instm/WebKitCSSMatrix/setMatrixValue\\n */\\nfunction WebKitCSSMatrix(opt_matrix) {}\\n\\n/**\\n * @constructor\\n * @param {string=} opt_matrix\\n * @extends {CSSMatrix}\\n * @see http://msdn.microsoft.com/en-us/library/windows/apps/hh453593.aspx\\n */\\nfunction MSCSSMatrix(opt_matrix) {}\\n","externs/w3c_elementtraversal.js":"/*\\n * Copyright 2009 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for DOM Element Traversal interface.\\n * This file depends on w3c_dom1.js.\\n * The whole file has been fully type annotated.\\n * Created from:\\n * http://www.w3.org/TR/ElementTraversal/#ecmascript-bindings\\n *\\n * @externs\\n * @author arv@google.com (Erik Arvidsson)\\n */\\n\\n/**\\n * @typedef {?(Document|DocumentFragment|Element)}\\n * @see https://dom.spec.whatwg.org/#parentnode\\n */\\nvar ParentNode;\\n\\n/**\\n * @typedef {?(Element|CharacterData)}\\n * @see https://dom.spec.whatwg.org/#nondocumenttypechildnode\\n */\\nvar NonDocumentTypeChildNode;\\n\\n/**\\n * @type {Element}\\n * @see https://developer.mozilla.org/En/DOM/Element.firstElementChild\\n */\\nElement.prototype.firstElementChild;\\n\\n/**\\n * @type {Element}\\n * @see https://developer.mozilla.org/En/DOM/Element.lastElementChild\\n */\\nElement.prototype.lastElementChild;\\n\\n/**\\n * @type {Element}\\n * @see https://developer.mozilla.org/En/DOM/Element.previousElementSibling\\n */\\nElement.prototype.previousElementSibling;\\n\\n/**\\n * @type {Element}\\n * @see https://developer.mozilla.org/En/DOM/Element.nextElementSibling\\n */\\nElement.prototype.nextElementSibling;\\n\\n/**\\n * @type {number}\\n * @see https://developer.mozilla.org/En/DOM/Element.childElementCount\\n */\\nElement.prototype.childElementCount;\\n\\n/**\\n * @type {?Element}\\n * @see https://dom.spec.whatwg.org/#dom-parentnode-firstelementchild\\n */\\nDocument.prototype.firstElementChild;\\n\\n/**\\n * @type {?Element}\\n * @see https://dom.spec.whatwg.org/#dom-parentnode-lastelementchild\\n */\\nDocument.prototype.lastElementChild;\\n\\n/**\\n * @type {number}\\n * @see https://dom.spec.whatwg.org/#dom-parentnode-childelementcount\\n */\\nDocument.prototype.childElementCount;\\n\\n/**\\n * @type {?Element}\\n * @see https://dom.spec.whatwg.org/#dom-parentnode-firstelementchild\\n */\\nDocumentFragment.prototype.firstElementChild;\\n\\n/**\\n * @type {?Element}\\n * @see https://dom.spec.whatwg.org/#dom-parentnode-lastelementchild\\n */\\nDocumentFragment.prototype.lastElementChild;\\n\\n/**\\n * @type {number}\\n * @see https://dom.spec.whatwg.org/#dom-parentnode-childelementcount\\n */\\nDocumentFragment.prototype.childElementCount;\\n\\n/**\\n * @type {?Element}\\n * @see https://dom.spec.whatwg.org/#dom-nondocumenttypechildnode-previouselementsibling\\n */\\nCharacterData.prototype.previousElementSibling;\\n\\n/**\\n * @type {?Element}\\n * @see https://dom.spec.whatwg.org/#dom-nondocumenttypechildnode-nextelementsibling\\n */\\nCharacterData.prototype.nextElementSibling;\\n","externs/w3c_gamepad.js":"/*\\n * Copyright 2013 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for W3C\'s Gamepad specification.\\n * @see http://www.w3.org/TR/gamepad/\\n * @externs\\n */\\n\\n/**\\n * @return {!Array.}\\n */\\nnavigator.getGamepads = function() {};\\n\\n/**\\n * @return {!Array.}\\n */\\nnavigator.webkitGetGamepads = function() {};\\n\\n\\n/**\\n * @interface\\n */\\nvar Gamepad = function() {};\\n\\n/**\\n * @type {string}\\n */\\nGamepad.prototype.id; // read-only\\n\\n/**\\n * @type {number}\\n */\\nGamepad.prototype.index; // read-only\\n\\n/**\\n * @type {boolean}\\n */\\nGamepad.prototype.connected; // read-only\\n\\n/**\\n * @type {number}\\n */\\nGamepad.prototype.timestamp; // read-only\\n\\n/**\\n * @type {string}\\n */\\nGamepad.prototype.mapping; // read-only\\n\\n/**\\n * @type {!Array.}\\n */\\nGamepad.prototype.axes; // read-only\\n\\n/**\\n * Note: The W3C spec changed, this property now returns an array of\\n * GamepadButton objects.\\n *\\n * @type {(!Array.|!Array.)}\\n */\\nGamepad.prototype.buttons;\\n\\n\\n/**\\n * @interface\\n */\\nvar GamepadButton = function() {};\\n\\n/**\\n * @type {boolean}\\n */\\nGamepadButton.prototype.pressed; // read-only\\n\\n/**\\n * @type {number}\\n */\\nGamepadButton.prototype.value; // read-only\\n","externs/w3c_geometry1.js":"/*\\n * Copyright 2018 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for W3C\'s Geometry Interfaces Module Level 1 spec.\\n * The whole file has been fully type annotated. Created from\\n * https://www.w3.org/TR/geometry-1/\\n *\\n * @externs\\n * @author bobak@google.com (Andreas F. Bobak)\\n */\\n\\n/**\\n * @deprecated ClientRect has been replaced by DOMRect in the latest spec.\\n * @constructor\\n * @see https://www.w3.org/TR/cssom-view/#changes-from-2011-08-04\\n */\\nfunction ClientRect() {}\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-clientrect-top\\n */\\nClientRect.prototype.top;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-clientrect-right\\n */\\nClientRect.prototype.right;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-clientrect-bottom\\n */\\nClientRect.prototype.bottom;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-clientrect-left\\n */\\nClientRect.prototype.left;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-clientrect-width\\n */\\nClientRect.prototype.width;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/cssom-view/#dom-clientrect-height\\n */\\nClientRect.prototype.height;\\n\\n/**\\n * @constructor\\n * @extends {ClientRect} for backwards compatibility\\n * @param {number=} x\\n * @param {number=} y\\n * @param {number=} width\\n * @param {number=} height\\n * @see https://www.w3.org/TR/geometry-1/#dom-domrectreadonly-domrectreadonly\\n */\\nfunction DOMRectReadOnly(x, y, width, height) {}\\n\\n/**\\n * @param {!DOMRectInit} other\\n * @return {!DOMRectReadOnly}\\n * @see https://www.w3.org/TR/geometry-1/#dom-domrectreadonly-fromrect\\n */\\nDOMRectReadOnly.prototype.fromRect = function(other) {};\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/geometry-1/#dom-domrectreadonly-x\\n */\\nDOMRectReadOnly.prototype.x;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/geometry-1/#dom-domrectreadonly-y\\n */\\nDOMRectReadOnly.prototype.y;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/geometry-1/#dom-domrectreadonly-width\\n */\\nDOMRectReadOnly.prototype.width;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/geometry-1/#dom-domrectreadonly-height\\n */\\nDOMRectReadOnly.prototype.height;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/geometry-1/#dom-domrectreadonly-top\\n */\\nDOMRectReadOnly.prototype.top;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/geometry-1/#dom-domrectreadonly-right\\n */\\nDOMRectReadOnly.prototype.right;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/geometry-1/#dom-domrectreadonly-bottom\\n */\\nDOMRectReadOnly.prototype.bottom;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/geometry-1/#dom-domrectreadonly-left\\n */\\nDOMRectReadOnly.prototype.left;\\n\\n/**\\n * @constructor\\n * @extends {DOMRectReadOnly}\\n * @param {number=} x\\n * @param {number=} y\\n * @param {number=} width\\n * @param {number=} height\\n * @see https://www.w3.org/TR/geometry-1/#dom-domrect-domrect\\n */\\nfunction DOMRect(x, y, width, height) {}\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/geometry-1/#dom-domrect-x\\n */\\nDOMRect.prototype.x;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/geometry-1/#dom-domrect-y\\n */\\nDOMRect.prototype.y;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/geometry-1/#dom-domrect-width\\n */\\nDOMRect.prototype.width;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/geometry-1/#dom-domrect-height\\n */\\nDOMRect.prototype.height;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/geometry-1/#dom-domrectreadonly-top\\n */\\nDOMRect.prototype.top;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/geometry-1/#dom-domrectreadonly-right\\n */\\nDOMRect.prototype.right;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/geometry-1/#dom-domrectreadonly-bottom\\n */\\nDOMRect.prototype.bottom;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/geometry-1/#dom-domrectreadonly-left\\n */\\nDOMRect.prototype.left;\\n\\n/**\\n * @constructor\\n * @see https://www.w3.org/TR/geometry-1/#dictdef-domrectinit\\n */\\nfunction DOMRectInit() {}\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/geometry-1/#dom-domrectinit-x\\n */\\nDOMRectInit.prototype.x;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/geometry-1/#dom-domrectinit-y\\n */\\nDOMRectInit.prototype.y;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/geometry-1/#dom-domrectinit-width\\n */\\nDOMRectInit.prototype.width;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/geometry-1/#dom-domrectinit-height\\n */\\nDOMRectInit.prototype.height;\\n","externs/w3c_geolocation.js":"/*\\n * Copyright 2009 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'; +a.a+="/**\\n * @fileoverview Definitions for W3C's Geolocation specification\\n * http://www.w3.org/TR/geolocation-API/\\n * @externs\\n * @author ngd@google.com (Neil Dunn)\\n */\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/geolocation-API/#geolocation\\n */\\nfunction Geolocation() {}\\n\\n/**\\n * @param {function(!GeolocationPosition)} successCallback\\n * @param {(function(!GeolocationPositionError)|null)=} opt_errorCallback\\n * @param {GeolocationPositionOptions=} opt_options\\n * @return {undefined}\\n */\\nGeolocation.prototype.getCurrentPosition = function(successCallback,\\n opt_errorCallback,\\n opt_options) {};\\n\\n/**\\n * @param {function(!GeolocationPosition)} successCallback\\n * @param {(function(!GeolocationPositionError)|null)=} opt_errorCallback\\n * @param {GeolocationPositionOptions=} opt_options\\n * @return {number}\\n */\\nGeolocation.prototype.watchPosition = function(successCallback,\\n opt_errorCallback,\\n opt_options) {};\\n\\n/**\\n * @param {number} watchId\\n * @return {undefined}\\n */\\nGeolocation.prototype.clearWatch = function(watchId) {};\\n\\n\\n/**\\n * @record\\n * @see http://www.w3.org/TR/geolocation-API/#coordinates\\n */\\nfunction GeolocationCoordinates() {}\\n/** @type {number} */\\nGeolocationCoordinates.prototype.latitude;\\n/** @type {number} */\\nGeolocationCoordinates.prototype.longitude;\\n/** @type {number} */\\nGeolocationCoordinates.prototype.accuracy;\\n/** @type {number} */\\nGeolocationCoordinates.prototype.altitude;\\n/** @type {number} */\\nGeolocationCoordinates.prototype.altitudeAccuracy;\\n/** @type {number} */\\nGeolocationCoordinates.prototype.heading;\\n/** @type {number} */\\nGeolocationCoordinates.prototype.speed;\\n\\n\\n/**\\n * @record\\n * @see http://www.w3.org/TR/geolocation-API/#position\\n */\\nfunction GeolocationPosition() {}\\n/** @type {GeolocationCoordinates} */\\nGeolocationPosition.prototype.coords;\\n/** @type {number} */\\nGeolocationPosition.prototype.timestamp;\\n\\n\\n/**\\n * @record\\n * @see http://www.w3.org/TR/geolocation-API/#position-options\\n */\\nfunction GeolocationPositionOptions() {}\\n/** @type {boolean|undefined} */\\nGeolocationPositionOptions.prototype.enableHighAccuracy;\\n/** @type {number|undefined} */\\nGeolocationPositionOptions.prototype.maximumAge;\\n/** @type {number|undefined} */\\nGeolocationPositionOptions.prototype.timeout;\\n\\n\\n/**\\n * @record\\n * @see http://www.w3.org/TR/geolocation-API/#position-error\\n */\\nfunction GeolocationPositionError() {}\\n/** @type {number} */\\nGeolocationPositionError.prototype.code;\\n/** @type {string} */\\nGeolocationPositionError.prototype.message;\\n/** @type {number} */\\nGeolocationPositionError.prototype.UNKNOWN_ERROR;\\n/** @type {number} */\\nGeolocationPositionError.prototype.PERMISSION_DENIED;\\n/** @type {number} */\\nGeolocationPositionError.prototype.POSITION_UNAVAILABLE;\\n/** @type {number} */\\nGeolocationPositionError.prototype.TIMEOUT;\\n\\n/** @type {Geolocation} */\\nNavigator.prototype.geolocation;\\n\",\"externs/w3c_indexeddb.js\":\"/*\\n * Copyright 2011 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for W3C's IndexedDB API and IndexedDB API 2.0.\\n * In Chrome all the IndexedDB classes are prefixed with 'webkit'.\\n * In order to access constants and static methods of these classes they must\\n * be duplicated with the prefix here.\\n * @see http://www.w3.org/TR/2015/REC-IndexedDB-20150108/\\n * @see https://www.w3.org/TR/2017/WD-IndexedDB-2-20170313/\\n *\\n * @externs\\n * @author guido.tapia@picnet.com.au (Guido Tapia)\\n * @author vobruba.martin@gmail.com (Martin Vobruba)\\n */\\n\\n/** @type {!IDBFactory} */\\nvar indexedDB;\\n\\n/** @type {!IDBFactory|undefined} */\\nWindow.prototype.moz_indexedDB;\\n\\n/** @type {!IDBFactory|undefined} */\\nWindow.prototype.mozIndexedDB;\\n\\n/** @type {!IDBFactory|undefined} */\\nWindow.prototype.webkitIndexedDB;\\n\\n/** @type {!IDBFactory|undefined} */\\nWindow.prototype.msIndexedDB;\\n\\n\\n\\n/**\\n * Possible values: 'readonly', 'readwrite', 'versionchange'\\n *\\n * @typedef {string}\\n * @see https://www.w3.org/TR/IndexedDB/#idl-def-IDBTransactionMode\\n */\\nvar IDBTransactionMode;\\n\\n\\n/**\\n * Possible values: 'pending', 'done'\\n *\\n * @typedef {string}\\n * @see https://www.w3.org/TR/IndexedDB/#idl-def-IDBRequestReadyState\\n */\\nvar IDBRequestReadyState;\\n\\n\\n/**\\n * Possible values: 'next', 'nextunique', 'prev', 'prevunique'\\n *\\n * @typedef {string}\\n * @see https://www.w3.org/TR/IndexedDB/#idl-def-IDBCursorDirection\\n */\\nvar IDBCursorDirection;\\n\\n\\n/**\\n * @record\\n * @see https://www.w3.org/TR/IndexedDB/#idl-def-IDBIndexParameters\\n */\\nfunction IDBIndexParameters(){};\\n\\n/** @type {(undefined|boolean)} */\\nIDBIndexParameters.prototype.unique;\\n\\n/** @type {(undefined|boolean)} */\\nIDBIndexParameters.prototype.multiEntry;\\n\\n\\n/**\\n * @record\\n * @extends {EventInit}\\n * @see https://www.w3.org/TR/IndexedDB/#idl-def-IDBVersionChangeEventInit\\n */\\nfunction IDBVersionChangeEventInit(){};\\n\\n/** @type {(undefined|number)} */\\nIDBVersionChangeEventInit.prototype.oldVersion;\\n\\n/** @type {(undefined|number|null)} */\\nIDBVersionChangeEventInit.prototype.newVersion;\\n\\n\\n\\n/**\\n * @record\\n * @see https://www.w3.org/TR/IndexedDB/#idl-def-IDBObjectStoreParameters\\n */\\nfunction IDBObjectStoreParameters() {};\\n\\n/** @type {(undefined|string|!Array|null)} */\\nIDBObjectStoreParameters.prototype.keyPath;\\n\\n/** @type {(undefined|boolean)} */\\nIDBObjectStoreParameters.prototype.autoIncrement;\\n\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBFactory\\n */\\nfunction IDBFactory() {}\\n\\n/**\\n * @param {string} name The name of the database to open.\\n * @param {number=} opt_version The version at which to open the database.\\n * @return {!IDBOpenDBRequest} The IDBRequest object.\\n */\\nIDBFactory.prototype.open = function(name, opt_version) {};\\n\\n/**\\n * @param {string} name The name of the database to delete.\\n * @return {!IDBOpenDBRequest} The IDBRequest object.\\n */\\nIDBFactory.prototype.deleteDatabase = function(name) {};\\n\\n/**\\n * @param {*} first\\n * @param {*} second\\n * @return {number}\\n */\\nIDBFactory.prototype.cmp = function(first, second) {};\\n\\n\\n/**\\n * @constructor\\n * @template T\\n * @implements {EventTarget}\\n * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBRequest\\n * @see https://www.w3.org/TR/IndexedDB-2/#request-api\\n */\\nfunction IDBRequest() {}\\n\\n/** @override */\\nIDBRequest.prototype.addEventListener = function(type, listener, opt_options) {\\n};\\n\\n/** @override */\\nIDBRequest.prototype.removeEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nIDBRequest.prototype.dispatchEvent = function(evt) {};\\n\\n\\n/**\\n * @constructor\\n * @extends {IDBRequest}\\n * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBRequest\\n * @see https://www.w3.org/TR/IndexedDB-2/#request-api\\n */\\nfunction webkitIDBRequest() {}\\n\\n/**\\n * @type {!IDBRequestReadyState}\\n */\\nIDBRequest.prototype.readyState; // readonly\\n\\n/**\\n * @type {function(!Event)}\\n */\\nIDBRequest.prototype.onsuccess = function(e) {};\\n\\n/**\\n * @type {function(!Event)}\\n */\\nIDBRequest.prototype.onerror = function(e) {};\\n\\n/** @type {T} */\\nIDBRequest.prototype.result; // readonly\\n\\n/**\\n * @type {number}\\n * @deprecated Use \\\"error\\\"\\n */\\nIDBRequest.prototype.errorCode; // readonly\\n\\n\\n/** @type {?DOMError|?DOMException} */\\nIDBRequest.prototype.error; // readonly\\n\\n/** @type {?IDBObjectStore|?IDBIndex|?IDBCursor} */\\nIDBRequest.prototype.source; // readonly\\n\\n/** @type {?IDBTransaction} */\\nIDBRequest.prototype.transaction; // readonly\\n\\n\\n/**\\n * @constructor\\n * @extends {IDBRequest}\\n * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBOpenDBRequest\\n */\\nfunction IDBOpenDBRequest() {}\\n\\n/**\\n * @type {function(!IDBVersionChangeEvent)}\\n */\\nIDBOpenDBRequest.prototype.onblocked = function(e) {};\\n\\n/**\\n * @type {function(!IDBVersionChangeEvent)}\\n */\\nIDBOpenDBRequest.prototype.onupgradeneeded = function(e) {};\\n\\n\\n/**\\n * @constructor\\n * @implements {EventTarget}\\n * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBDatabase\\n * @see https://www.w3.org/TR/IndexedDB-2/#database-interface\\n */\\nfunction IDBDatabase() {}\\n\\n/**\\n * @type {string}\\n * @const\\n */\\nIDBDatabase.prototype.name;\\n\\n/**\\n * @type {number}\\n * @const\\n */\\nIDBDatabase.prototype.version;\\n\\n/**\\n * @type {!DOMStringList}\\n * @const\\n */\\nIDBDatabase.prototype.objectStoreNames;\\n\\n/**\\n * @param {string} name The name of the object store.\\n * @param {!IDBObjectStoreParameters=} opt_parameters Parameters to be passed\\n * creating the object store.\\n * @return {!IDBObjectStore} The created/open object store.\\n */\\nIDBDatabase.prototype.createObjectStore =\\n function(name, opt_parameters) {};\\n\\n/**\\n * @param {string} name The name of the object store to remove.\\n * @return {undefined}\\n */\\nIDBDatabase.prototype.deleteObjectStore = function(name) {};\\n\\n/**\\n * @param {(string|!Array|!DOMStringList)} storeNames The stores to open\\n * in this transaction.\\n * @param {!IDBTransactionMode=} mode The mode for opening the object stores.\\n * @return {!IDBTransaction} The IDBRequest object.\\n */\\nIDBDatabase.prototype.transaction = function(storeNames, mode) {};\\n\\n/**\\n * Closes the database connection.\\n * @return {undefined}\\n */\\nIDBDatabase.prototype.close = function() {};\\n\\n/**\\n * @type {?function(!Event)}\\n */\\nIDBDatabase.prototype.onabort;\\n\\n/**\\n * @type {?function(!Event)}\\n */\\nIDBDatabase.prototype.onclose;\\n\\n/**\\n * @type {?function(!Event)}\\n */\\nIDBDatabase.prototype.onerror;\\n\\n/**\\n * @type {?function(!IDBVersionChangeEvent)}\\n */\\nIDBDatabase.prototype.onversionchange;\\n\\n/** @override */\\nIDBDatabase.prototype.addEventListener = function(type, listener, opt_options) {\\n};\\n\\n/** @override */\\nIDBDatabase.prototype.removeEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nIDBDatabase.prototype.dispatchEvent = function(evt) {};\\n\\n\\n/**\\n * Typedef for valid key types according to the w3 specification. Note that this\\n * is slightly wider than what is actually allowed, as all Array elements must\\n * have a valid key type.\\n * @see http://www.w3.org/TR/IndexedDB/#key-construct\\n * @see https://www.w3.org/TR/IndexedDB-2/#key-construct\\n * @typedef {number|string|!Date|!Array|!BufferSource}\\n */\\nvar IDBKeyType;\\n\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBObjectStore\\n * @see https://www.w3.org/TR/IndexedDB-2/#object-store-interface\\n */\\nfunction IDBObjectStore() {}\\n\\n/**\\n * @type {string}\\n */\\nIDBObjectStore.prototype.name;\\n\\n/**\\n * @type {*}\\n */\\nIDBObjectStore.prototype.keyPath;\\n\\n/**\\n * @type {!DOMStringList}\\n */\\nIDBObjectStore.prototype.indexNames;\\n\\n/** @type {!IDBTransaction} */\\nIDBObjectStore.prototype.transaction;\\n\\n/** @type {boolean} */\\nIDBObjectStore.prototype.autoIncrement;\\n\\n/**\\n * @param {*} value The value to put into the object store.\\n * @param {!IDBKeyType=} key The key of this value.\\n * @return {!IDBRequest} The IDBRequest object.\\n */\\nIDBObjectStore.prototype.put = function(value, key) {};\\n\\n/**\\n * @param {*} value The value to add into the object store.\\n * @param {!IDBKeyType=} key The key of this value.\\n * @return {!IDBRequest} The IDBRequest object.\\n */\\nIDBObjectStore.prototype.add = function(value, key) {};\\n\\n/**\\n * @param {!IDBKeyType|!IDBKeyRange} key The key of this value.\\n * @return {!IDBRequest} The IDBRequest object.\\n */\\nIDBObjectStore.prototype.delete = function(key) {};\\n\\n/**\\n * @param {!IDBKeyType|!IDBKeyRange} key The key of the document to retrieve.\\n * @return {!IDBRequest} The IDBRequest object.\\n */\\nIDBObjectStore.prototype.get = function(key) {};\\n\\n/**\\n * @return {!IDBRequest} The IDBRequest object.\\n */\\nIDBObjectStore.prototype.clear = function() {};\\n\\n/**\\n * @param {?IDBKeyRange=} range The range of the cursor.\\n * Nullable because IE <11 has problems with undefined.\\n * @param {!IDBCursorDirection=} direction The direction of cursor enumeration.\\n * @return {!IDBRequest} The IDBRequest object.\\n */\\nIDBObjectStore.prototype.openCursor = function(range, direction) {};\\n\\n/**\\n * @param {string} name The name of the index.\\n * @param {string|!Array} keyPath The path to the index key.\\n * @param {!IDBIndexParameters=} opt_paramters Optional parameters\\n * for the created index.\\n * @return {!IDBIndex} The IDBIndex object.\\n */\\nIDBObjectStore.prototype.createIndex = function(name, keyPath, opt_paramters) {};\\n\\n/**\\n * @param {string} name The name of the index to retrieve.\\n * @return {!IDBIndex} The IDBIndex object.\\n */\\nIDBObjectStore.prototype.index = function(name) {};\\n\\n/**\\n * @param {string} indexName The name of the index to remove.\\n * @return {undefined}\\n */\\nIDBObjectStore.prototype.deleteIndex = function(indexName) {};\\n\\n/**\\n * @param {(!IDBKeyType|IDBKeyRange)=} key The key of this value.\\n * @return {!IDBRequest} The IDBRequest object.\\n * @see http://www.w3.org/TR/IndexedDB/#widl-IDBObjectStore-count\\n */\\nIDBObjectStore.prototype.count = function(key) {};\\n\\n/**\\n * @param {(!IDBKeyType|IDBKeyRange)=} query\\n * @return {!IDBRequest} The IDBRequest object.\\n * @see https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-getkey\\n */\\nIDBObjectStore.prototype.getKey = function(query) {};\\n\\n/**\\n * @param {(!IDBKeyType|IDBKeyRange)=} query\\n * @param {number=} count\\n * @return {!IDBRequest} The IDBRequest object.\\n * @see https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-getall\\n */\\nIDBObjectStore.prototype.getAll = function(query, count) {};\\n\\n/**\\n * @param {(!IDBKeyType|IDBKeyRange)=} query\\n * @param {number=} count\\n * @return {!IDBRequest} The IDBRequest object.\\n * @see https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-getallkeys\\n */\\nIDBObjectStore.prototype.getAllKeys = function(query, count) {};\\n\\n/**\\n * @param {(!IDBKeyType|IDBKeyRange)=} query\\n * @param {!IDBCursorDirection=} direction\\n * @return {!IDBRequest} The IDBRequest object.\\n * @see https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-openkeycursor\\n */\\nIDBObjectStore.prototype.openKeyCursor = function(query, direction) {};\\n\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBIndex\\n * @see https://www.w3.org/TR/IndexedDB-2/#index-interface\\n */\\nfunction IDBIndex() {}\\n\\n/**\\n * @type {string}\\n */\\nIDBIndex.prototype.name;\\n\\n/**\\n * @type {!IDBObjectStore}\\n * @const\\n */\\nIDBIndex.prototype.objectStore;\\n\\n/**\\n * @type {*}\\n * @const\\n */\\nIDBIndex.prototype.keyPath;\\n\\n/**\\n * @type {boolean}\\n * @const\\n */\\nIDBIndex.prototype.multiEntry;\\n\\n/**\\n * @type {boolean}\\n * @const\\n */\\nIDBIndex.prototype.unique;\\n\\n/**\\n * @param {(!IDBKeyType|?IDBKeyRange)=} range The range of the cursor.\\n * Nullable because IE <11 has problems with undefined.\\n * @param {!IDBCursorDirection=} direction The direction of cursor enumeration.\\n * @return {!IDBRequest} The IDBRequest object.\\n */\\nIDBIndex.prototype.openCursor = function(range, direction) {};\\n\\n/**\\n * @param {(!IDBKeyType|?IDBKeyRange)=} range The range of the cursor.\\n * Nullable because IE <11 has problems with undefined.\\n * @param {!IDBCursorDirection=} direction The direction of cursor enumeration.\\n * @return {!IDBRequest} The IDBRequest object.\\n */\\nIDBIndex.prototype.openKeyCursor = function(range, direction) {};\\n\\n/**\\n * @param {!IDBKeyType|!IDBKeyRange} key The id of the object to retrieve.\\n * @return {!IDBRequest} The IDBRequest object.\\n */\\nIDBIndex.prototype.get = function(key) {};\\n\\n/**\\n * @param {!IDBKeyType|!IDBKeyRange} key The id of the object to retrieve.\\"; +a.a+='n * @return {!IDBRequest} The IDBRequest object.\\n */\\nIDBIndex.prototype.getKey = function(key) {};\\n\\n/**\\n * @param {(!IDBKeyType|!IDBKeyRange)=} query\\n * @param {number=} count\\n * @return {!IDBRequest}\\n * @see https://www.w3.org/TR/IndexedDB-2/#dom-idbindex-getall\\n */\\nIDBIndex.prototype.getAll = function(query, count) {};\\n\\n/**\\n * @param {(!IDBKeyType|!IDBKeyRange)=} query\\n * @param {number=} count\\n * @return {!IDBRequest}\\n * @see https://www.w3.org/TR/IndexedDB-2/#dom-idbindex-getallkeys\\n */\\nIDBIndex.prototype.getAllKeys = function(query, count) {};\\n\\n/**\\n * @param {(!IDBKeyType|!IDBKeyRange)=} opt_key\\n * @return {!IDBRequest}\\n */\\nIDBIndex.prototype.count = function(opt_key) {};\\n\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBCursor\\n * @see https://www.w3.org/TR/IndexedDB-2/#cursor-interface\\n */\\nfunction IDBCursor() {}\\n\\n/**\\n * @constructor\\n * @extends {IDBCursor}\\n * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBCursor\\n * @see https://www.w3.org/TR/IndexedDB-2/#cursor-interface\\n */\\nfunction webkitIDBCursor() {}\\n\\n/**\\n * @type {(!IDBObjectStore|!IDBIndex)}\\n * @const\\n */\\nIDBCursor.prototype.source;\\n\\n/**\\n * @type {!IDBCursorDirection}\\n * @const\\n */\\nIDBCursor.prototype.direction;\\n\\n/**\\n * @type {!IDBKeyType}\\n * @const\\n */\\nIDBCursor.prototype.key;\\n\\n/**\\n * @type {!IDBKeyType}\\n * @const\\n */\\nIDBCursor.prototype.primaryKey;\\n\\n/**\\n * @param {*} value The new value for the current object in the cursor.\\n * @return {!IDBRequest} The IDBRequest object.\\n */\\nIDBCursor.prototype.update = function(value) {};\\n\\n/**\\n * Note: Must be quoted to avoid parse error.\\n * @param {!IDBKeyType=} key Continue enumerating the cursor from the specified\\n * key (or next).\\n * @return {undefined}\\n */\\nIDBCursor.prototype.continue = function(key) {};\\n\\n/**\\n * @param {!IDBKeyType} key\\n * @param {!IDBKeyType} primaryKey\\n * @return {undefined}\\n * @see https://www.w3.org/TR/IndexedDB-2/#dom-idbcursor-continueprimarykey\\n */\\nIDBCursor.prototype.continuePrimaryKey = function(key, primaryKey) {};\\n\\n/**\\n * @param {number} count Number of times to iterate the cursor.\\n * @return {undefined}\\n */\\nIDBCursor.prototype.advance = function(count) {};\\n\\n/**\\n * Note: Must be quoted to avoid parse error.\\n * @return {!IDBRequest} The IDBRequest object.\\n */\\nIDBCursor.prototype.delete = function() {};\\n\\n\\n/**\\n * @constructor\\n * @extends {IDBCursor}\\n * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBCursorWithValue\\n */\\nfunction IDBCursorWithValue() {}\\n\\n/** @type {*} */\\nIDBCursorWithValue.prototype.value; // readonly\\n\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBTransaction\\n * @see https://www.w3.org/TR/IndexedDB-2/#transaction\\n */\\nfunction IDBTransaction() {}\\n\\n/**\\n * @constructor\\n * @extends {IDBTransaction}\\n * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBTransaction\\n * @see https://www.w3.org/TR/IndexedDB-2/#transaction\\n */\\nfunction webkitIDBTransaction() {}\\n\\n/**\\n * @type {!DOMStringList}\\n * @const\\n */\\nIDBTransaction.prototype.objectStoreNames;\\n\\n/**\\n * @type {!IDBTransactionMode}\\n * @const\\n */\\nIDBTransaction.prototype.mode;\\n\\n/**\\n * @type {!IDBDatabase}\\n * @const\\n */\\nIDBTransaction.prototype.db;\\n\\n/**\\n * @type {!DOMError|!DOMException}\\n */\\nIDBTransaction.prototype.error;\\n\\n/**\\n * @param {string} name The name of the object store to retrieve.\\n * @return {!IDBObjectStore} The object store.\\n */\\nIDBTransaction.prototype.objectStore = function(name) {};\\n\\n/**\\n * Aborts the transaction.\\n * @return {undefined}\\n */\\nIDBTransaction.prototype.abort = function() {};\\n\\n/**\\n * @type {?function(!Event)}\\n */\\nIDBTransaction.prototype.onabort;\\n\\n/**\\n * @type {?function(!Event)}\\n */\\nIDBTransaction.prototype.oncomplete;\\n\\n/**\\n * @type {?function(!Event)}\\n */\\nIDBTransaction.prototype.onerror;\\n\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBKeyRange\\n * @see https://www.w3.org/TR/IndexedDB-2/#keyrange\\n */\\nfunction IDBKeyRange() {}\\n\\n/**\\n * @constructor\\n * @extends {IDBKeyRange}\\n * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBKeyRange\\n * @see https://www.w3.org/TR/IndexedDB-2/#keyrange\\n */\\nfunction webkitIDBKeyRange() {}\\n\\n/**\\n * @type {*}\\n * @const\\n */\\nIDBKeyRange.prototype.lower;\\n\\n/**\\n * @type {*}\\n * @const\\n */\\nIDBKeyRange.prototype.upper;\\n\\n/**\\n * @type {boolean}\\n * @const\\n */\\nIDBKeyRange.prototype.lowerOpen;\\n\\n/**\\n * @type {boolean}\\n * @const\\n */\\nIDBKeyRange.prototype.upperOpen;\\n\\n/**\\n * @param {!IDBKeyType} value The single key value of this range.\\n * @return {!IDBKeyRange} The key range.\\n */\\nIDBKeyRange.only = function(value) {};\\n\\n/**\\n * @param {!IDBKeyType} bound Creates a lower bound key range.\\n * @param {boolean=} open Open the key range.\\n * @return {!IDBKeyRange} The key range.\\n */\\nIDBKeyRange.lowerBound = function(bound, open) {};\\n\\n/**\\n * @param {!IDBKeyType} bound Creates an upper bound key range.\\n * @param {boolean=} open Open the key range.\\n * @return {!IDBKeyRange} The key range.\\n */\\nIDBKeyRange.upperBound = function(bound, open) {};\\n\\n/**\\n * @param {!IDBKeyType} left The left bound value.\\n * @param {!IDBKeyType} right The right bound value.\\n * @param {boolean=} openLeft Whether the left bound value should be excluded.\\n * @param {boolean=} openRight Whether the right bound value should be excluded.\\n * @return {!IDBKeyRange} The key range.\\n */\\nIDBKeyRange.bound = function(left, right, openLeft, openRight) {};\\n\\n/**\\n * @param {!IDBKeyType} key\\n * @return {boolean}\\n * @see https://www.w3.org/TR/IndexedDB-2/#dom-idbkeyrange-includes\\n */\\nIDBKeyRange.prototype.includes = function(key) {};\\n\\n\\n/**\\n * @param {string} type\\n * @param {!IDBVersionChangeEventInit=} opt_eventInit\\n * @constructor\\n * @extends {Event}\\n * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBVersionChangeEvent\\n */\\nfunction IDBVersionChangeEvent(type, opt_eventInit) {}\\n\\n/**\\n * @type {number}\\n * @const\\n */\\nIDBVersionChangeEvent.prototype.oldVersion;\\n\\n/**\\n * @type {?number}\\n * @const\\n */\\nIDBVersionChangeEvent.prototype.newVersion;\\n\\n\\n/**\\n * @param {string} type\\n * @param {!IDBVersionChangeEventInit=} opt_eventInit\\n * @constructor\\n * @extends {IDBVersionChangeEvent}\\n * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBVersionChangeEvent\\n */\\nfunction webkitIDBVersionChangeEvent(type, opt_eventInit) {}\\n\\n/**\\n * @type {string}\\n * @const\\n */\\nwebkitIDBVersionChangeEvent.prototype.version;\\n","externs/w3c_midi.js":"/*\\n * Copyright 2014 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview W3C Web MIDI specification.\\n * @see http://www.w3.org/TR/webmidi/\\n *\\n * @externs\\n */\\n\\n\\n/**\\n * @param {!MIDIOptions=} opt_options\\n * @return {!Promise.}\\n */\\nnavigator.requestMIDIAccess = function(opt_options) {};\\n\\n\\n/**\\n * @typedef {{\\n * sysex: boolean\\n * }}\\n */\\nvar MIDIOptions;\\n\\n\\n\\n/**\\n * @interface\\n */\\nvar MIDIInputMap = function() {};\\n\\n\\n/**\\n * @const {number}\\n */\\nMIDIInputMap.prototype.size;\\n\\n\\n/**\\n * @param {function(string)} iterator\\n */\\nMIDIInputMap.prototype.keys = function(iterator) {};\\n\\n\\n/**\\n * @param {function(!Array.<*>)} iterator\\n */\\nMIDIInputMap.prototype.entries = function(iterator) {};\\n\\n\\n/**\\n * @param {function(!MIDIInput)} iterator\\n */\\nMIDIInputMap.prototype.values = function(iterator) {};\\n\\n\\n/**\\n * @param {string} key\\n * @return {!MIDIInput}\\n */\\nMIDIInputMap.prototype.get = function(key) {};\\n\\n\\n/**\\n * @param {string} key\\n * @return {boolean}\\n */\\nMIDIInputMap.prototype.has = function(key) {};\\n\\n\\n\\n/**\\n * @interface\\n */\\nvar MIDIOutputMap = function() {};\\n\\n\\n/**\\n * @const {number}\\n */\\nMIDIOutputMap.prototype.size;\\n\\n\\n/**\\n * @param {function(string)} iterator\\n */\\nMIDIOutputMap.prototype.keys = function(iterator) {};\\n\\n\\n/**\\n * @param {function(!Array.<*>)} iterator\\n */\\nMIDIOutputMap.prototype.entries = function(iterator) {};\\n\\n\\n/**\\n * @param {function(!MIDIOutput)} iterator\\n */\\nMIDIOutputMap.prototype.values = function(iterator) {};\\n\\n\\n/**\\n * @param {string} key\\n * @return {!MIDIOutput}\\n */\\nMIDIOutputMap.prototype.get = function(key) {};\\n\\n\\n/**\\n * @param {string} key\\n * @return {boolean}\\n */\\nMIDIOutputMap.prototype.has = function(key) {};\\n\\n\\n\\n/**\\n * @interface\\n * @extends {EventTarget}\\n */\\nvar MIDIAccess = function() {};\\n\\n\\n/**\\n * @const {!MIDIInputMap}\\n */\\nMIDIAccess.prototype.inputs;\\n\\n\\n/**\\n * @const {!MIDIOutputMap}\\n */\\nMIDIAccess.prototype.outputs;\\n\\n\\n/**\\n * @const {function(!MIDIConnectionEvent)}\\n */\\nMIDIAccess.prototype.onconnect;\\n\\n\\n/**\\n * @type {function(!MIDIConnectionEvent)}\\n */\\nMIDIAccess.prototype.ondisconnect;\\n\\n\\n/**\\n * @const {boolean}\\n */\\nMIDIAccess.prototype.sysexEnabled;\\n\\n\\n\\n/**\\n * @interface\\n * @extends {EventTarget}\\n */\\nvar MIDIPort = function() {};\\n\\n\\n/**\\n * @const {string}\\n */\\nMIDIPort.prototype.id;\\n\\n\\n/**\\n * @const {string}\\n */\\nMIDIPort.prototype.manufacturer;\\n\\n\\n/**\\n * @const {string}\\n */\\nMIDIPort.prototype.name;\\n\\n\\n/**\\n * @const {string}\\n */\\nMIDIPort.prototype.type;\\n\\n\\n/**\\n * @const {string}\\n */\\nMIDIPort.prototype.version;\\n\\n\\n/**\\n * @type {function(!MIDIConnectionEvent)}\\n */\\nMIDIPort.prototype.ondisconnect;\\n\\n\\n\\n/**\\n * @interface\\n * @extends {MIDIPort}\\n */\\nvar MIDIInput = function() {};\\n\\n\\n/**\\n * @type {function(!MIDIMessageEvent)}\\n */\\nMIDIInput.prototype.onmidimessage;\\n\\n\\n\\n/**\\n * @interface\\n * @extends {MIDIPort}\\n */\\nvar MIDIOutput = function() {};\\n\\n\\n/**\\n * @param {!Uint8Array} data\\n * @param {number=} opt_timestamp\\n */\\nMIDIOutput.prototype.send = function(data, opt_timestamp) {};\\n\\n\\n\\n/**\\n * @constructor\\n * @extends {Event}\\n * @param {string} type\\n * @param {!MIDIMessageEventInit=} opt_init\\n */\\nvar MIDIMessageEvent = function(type, opt_init) {};\\n\\n\\n/**\\n * @const {number}\\n */\\nMIDIMessageEvent.prototype.receivedTime;\\n\\n\\n/**\\n * @const {!Uint8Array}\\n */\\nMIDIMessageEvent.prototype.data;\\n\\n\\n/**\\n * @record\\n * @extends {EventInit}\\n * @see https://www.w3.org/TR/webmidi/#midimessageeventinit-interface\\n */\\nfunction MIDIMessageEventInit() {}\\n\\n/** @type {undefined|number} */\\nMIDIMessageEventInit.prototype.receivedTime;\\n\\n/** @type {undefined|!Uint8Array} */\\nMIDIMessageEventInit.prototype.data;\\n\\n\\n\\n/**\\n * @constructor\\n * @extends {Event}\\n * @param {string} type\\n * @param {!MIDIConnectionEventInit=} opt_init\\n */\\nvar MIDIConnectionEvent = function(type, opt_init) {};\\n\\n\\n/**\\n * @const {MIDIPort}\\n */\\nMIDIConnectionEvent.prototype.port;\\n\\n\\n/**\\n * @record\\n * @extends {EventInit}\\n * @see https://www.w3.org/TR/webmidi/#idl-def-MIDIConnectionEventInit\\n */\\nfunction MIDIConnectionEventInit() {}\\n\\n/** @type {undefined|!MIDIPort} */\\nMIDIConnectionEventInit.prototype.port;\\n","externs/w3c_navigation_timing.js":"/*\\n * Copyright 2011 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for W3C\'s Navigation Timing specification.\\n *\\n * Created from\\n * @see http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html\\n * @see http://w3c-test.org/webperf/specs/ResourceTiming\\n * @see http://www.w3.org/TR/performance-timeline\\n * @see http://www.w3.org/TR/user-timing/\\n *\\n * @externs\\n * @author rky@google.com (Ren\u00e9 Kyllingstad)\\n */\\n\\n/** @constructor */\\nfunction PerformanceTiming() {}\\n/** @type {number} */ PerformanceTiming.prototype.navigationStart;\\n/** @type {number} */ PerformanceTiming.prototype.unloadEventStart;\\n/** @type {number} */ PerformanceTiming.prototype.unloadEventEnd;\\n/** @type {number} */ PerformanceTiming.prototype.redirectStart;\\n/** @type {number} */ PerformanceTiming.prototype.redirectEnd;\\n/** @type {number} */ PerformanceTiming.prototype.fetchStart;\\n/** @type {number} */ PerformanceTiming.prototype.domainLookupStart;\\n/** @type {number} */ PerformanceTiming.prototype.domainLookupEnd;\\n/** @type {number} */ PerformanceTiming.prototype.connectStart;\\n/** @type {number} */ PerformanceTiming.prototype.connectEnd;\\n/** @type {number} */ PerformanceTiming.prototype.secureConnectionStart;\\n/** @type {number} */ PerformanceTiming.prototype.requestStart;\\n/** @type {number} */ PerformanceTiming.prototype.responseStart;\\n/** @type {number} */ PerformanceTiming.prototype.responseEnd;\\n/** @type {number} */ PerformanceTiming.prototype.domLoading;\\n/** @type {number} */ PerformanceTiming.prototype.domInteractive;\\n/** @type {number} */ PerformanceTiming.prototype.domContentLoadedEventStart;\\n/** @type {number} */ PerformanceTiming.prototype.domContentLoadedEventEnd;\\n/** @type {number} */ PerformanceTiming.prototype.domComplete;\\n/** @type {number} */ PerformanceTiming.prototype.loadEventStart;\\n/** @type {number} */ PerformanceTiming.prototype.loadEventEnd;\\n\\n/** @constructor */\\nfunction PerformanceEntry() {}\\n/** @type {string} */ PerformanceEntry.prototype.name;\\n/** @type {string} */ PerformanceEntry.prototype.entryType;\\n/** @type {number} */ PerformanceEntry.prototype.startTime;\\n/** @type {number} */ PerformanceEntry.prototype.duration;\\n\\n/**\\n * https://www.w3.org/TR/resource-timing-2/#performanceresourcetiming\\n * @constructor\\n * @extends {PerformanceEntry}\\n */\\nfunction PerformanceResourceTiming() {}\\n/** @type {number} */ PerformanceResourceTiming.prototype.redirectStart;\\n/** @type {number} */ PerformanceResourceTiming.prototype.redirectEnd;\\n/** @type {number} */ PerformanceResourceTiming.prototype.fetchStart;\\n/** @type {number} */ PerformanceResourceTiming.prototype.domainLookupStart;\\n/** @type {number} */ PerformanceResourceTiming.prototype.domainLookupEnd;\\n/** @type {number} */ PerformanceResourceTiming.prototype.connectStart;\\n/** @type {number} */ PerformanceResourceTiming.prototype.connectEnd;\\n/** @type {number} */\\nPerformanceResourceTiming.prototype.secureConnectionStart;\\n/** @type {number} */ PerformanceResourceTiming.prototype.requestStart;\\n/** @type {number} */ PerformanceResourceTiming.prototype.responseStart;\\n/** @type {number} */ PerformanceResourceTiming.prototype.responseEnd;\\n/** @type {string} */ PerformanceResourceTiming.prototype.initiatorType;\\n/** @type {number|undefined} */\\nPerformanceResourceTiming.prototype.transferSize;\\n/** @type {number|undefined} */\\nPerformanceResourceTiming.prototype.encodedBodySize;\\n/** @type {number|undefined} */\\nPerformanceResourceTiming.prototype.decodedBodySize;\\n/** @type {number|undefined} */\\nPerformanceResourceTiming.prototype.workerStart;\\n/** @type {string} */ PerformanceResourceTiming.prototype.nextHopProtocol;\\n\\n/**\\n * Possible values are \'navigate\', \'reload\', \'back_forward\', and \'prerender\'.\\n * See https://w3c.github.io/navigation-timing/#sec-performance-navigation-types\\n * @typedef {string}\\n */\\nvar NavigationType;\\n\\n/**\\n * https://w3c.github.io/navigation-timing/#sec-PerformanceNavigationTiming\\n * @constructor\\n * @extends {PerformanceResourceTiming}\\n */\\nfunction PerformanceNavigationTiming() {}\\n/** @type {number} */ PerformanceNavigationTiming.prototype.unloadEventStart;\\n/** @type {number} */ PerformanceNavigationTiming.prototype.unloadEventEnd;\\n/** @type {number} */ PerformanceNavigationTiming.prototype.domIntera'; +a.a+="ctive;\\n/** @type {number} */ PerformanceNavigationTiming.prototype\\n .domContentLoadedEventStart;\\n/** @type {number} */ PerformanceNavigationTiming.prototype\\n .domContentLoadedEventEnd;\\n/** @type {number} */ PerformanceNavigationTiming.prototype.domComplete;\\n/** @type {number} */ PerformanceNavigationTiming.prototype.loadEventStart;\\n/** @type {number} */ PerformanceNavigationTiming.prototype.loadEventEnd;\\n/** @type {NavigationType} */ PerformanceNavigationTiming.prototype.type;\\n/** @type {number} */ PerformanceNavigationTiming.prototype.redirectCount;\\n\\n/** @constructor */\\nfunction PerformanceNavigation() {}\\n/** @type {number} */ PerformanceNavigation.prototype.TYPE_NAVIGATE = 0;\\n/** @type {number} */ PerformanceNavigation.prototype.TYPE_RELOAD = 1;\\n/** @type {number} */ PerformanceNavigation.prototype.TYPE_BACK_FORWARD = 2;\\n/** @type {number} */ PerformanceNavigation.prototype.TYPE_RESERVED = 255;\\n/** @type {number} */ PerformanceNavigation.prototype.type;\\n/** @type {number} */ PerformanceNavigation.prototype.redirectCount;\\n\\n/**\\n * https://w3c.github.io/longtasks/#taskattributiontiming\\n * @constructor\\n * @extends {PerformanceEntry}\\n */\\nfunction TaskAttributionTiming() {}\\n/** @type {string} */ TaskAttributionTiming.prototype.containerId;\\n/** @type {string} */ TaskAttributionTiming.prototype.containerName;\\n/** @type {string} */ TaskAttributionTiming.prototype.containerSrc;\\n/** @type {string} */ TaskAttributionTiming.prototype.containerType;\\n\\n/**\\n * https://w3c.github.io/longtasks/#performancelongtasktiming\\n * @constructor\\n * @extends {PerformanceEntry}\\n */\\nfunction PerformanceLongTaskTiming() {}\\n/** @type {!Array} */\\nPerformanceLongTaskTiming.prototype.attribution;\\n\\n\\n/** @constructor */\\nfunction Performance() {}\\n\\n/** @type {PerformanceTiming} */\\nPerformance.prototype.timing;\\n\\n/** @type {PerformanceNavigation} */\\nPerformance.prototype.navigation;\\n\\n/** @type {number} */\\nPerformance.prototype.timeOrigin;\\n\\n\\n/**\\n * Clears the buffer used to store the current list of\\n * PerformanceResourceTiming resources.\\n * @return {undefined}\\n */\\nPerformance.prototype.clearResourceTimings = function() {};\\n\\n/**\\n * Clear out the buffer of performance timing events for webkit browsers.\\n * @return {undefined}\\n */\\nPerformance.prototype.webkitClearResourceTimings = function() {};\\n\\n/**\\n * A callback that is invoked when the resourcetimingbufferfull event is fired.\\n * @type {?function(Event)}\\n */\\nPerformance.prototype.onresourcetimingbufferfull = function() {};\\n\\n/**\\n * Set the maximum number of PerformanceResourceTiming resources that may be\\n * stored in the buffer.\\n * @param {number} maxSize\\n * @return {undefined}\\n */\\nPerformance.prototype.setResourceTimingBufferSize = function(maxSize) {};\\n\\n/**\\n * @return {!Array} A copy of the PerformanceEntry list,\\n * in chronological order with respect to startTime.\\n * @nosideeffects\\n */\\nPerformance.prototype.getEntries = function() {};\\n\\n/**\\n * @param {string} entryType Only return `PerformanceEntry`s with this\\n * entryType.\\n * @return {!Array} A copy of the PerformanceEntry list,\\n * in chronological order with respect to startTime.\\n * @nosideeffects\\n */\\nPerformance.prototype.getEntriesByType = function(entryType) {};\\n\\n/**\\n * @param {string} name Only return `PerformanceEntry`s with this name.\\n * @param {string=} opt_entryType Only return `PerformanceEntry`s with\\n * this entryType.\\n * @return {!Array} PerformanceEntry list in chronological\\n * order with respect to startTime.\\n * @nosideeffects\\n */\\nPerformance.prototype.getEntriesByName = function(name, opt_entryType) {};\\n\\n// Nonstandard. Only available in Blink.\\n// Returns more granular results with the --enable-memory-info flag.\\n/** @type {MemoryInfo} */ Performance.prototype.memory;\\n\\n/**\\n * @return {number}\\n * @nosideeffects\\n */\\nPerformance.prototype.now = function() {};\\n\\n/**\\n * @return {number}\\n * @nosideeffects\\n */\\nPerformance.prototype.webkitNow = function() {};\\n\\n/**\\n * @param {string} markName\\n * @return {undefined}\\n */\\nPerformance.prototype.mark = function(markName) {};\\n\\n/**\\n * @param {string=} opt_markName\\n * @return {undefined}\\n */\\nPerformance.prototype.clearMarks = function(opt_markName) {};\\n\\n/**\\n * @param {string} measureName\\n * @param {string=} opt_startMark\\n * @param {string=} opt_endMark\\n * @return {undefined}\\n */\\nPerformance.prototype.measure = function(\\n measureName, opt_startMark, opt_endMark) {};\\n\\n/**\\n * @param {string=} opt_measureName\\n * @return {undefined}\\n */\\nPerformance.prototype.clearMeasures = function(opt_measureName) {};\\n\\n/** @type {Performance} */\\nWindow.prototype.performance;\\n\\n/**\\n * @type {!Performance}\\n * @suppress {duplicate}\\n */\\nvar performance;\\n\\n/**\\n * @constructor\\n * @extends {Performance}\\n */\\nfunction WorkerPerformance() {}\\n\\n/**\\n * @typedef {function(!PerformanceObserverEntryList, !PerformanceObserver): void}\\n */\\nvar PerformanceObserverCallback;\\n\\n/**\\n * See:\\n * https://w3c.github.io/performance-timeline/#the-performanceobserver-interface\\n * @constructor\\n * @param {!PerformanceObserverCallback} callback\\n */\\nfunction PerformanceObserver(callback) {}\\n\\n/**\\n * @param {!PerformanceObserverInit} options\\n */\\nPerformanceObserver.prototype.observe = function(options) {};\\n\\n/** @return {void} */\\nPerformanceObserver.prototype.disconnect = function() {};\\n\\n/**\\n * See https://developer.mozilla.org/en-US/docs/Web/API/PerformanceObserver/takeRecords\\n * @return {!PerformanceObserverEntryList}\\n */\\nPerformanceObserver.prototype.takeRecords = function() {};\\n\\n/**\\n * @record\\n */\\nfunction PerformanceObserverInit() {}\\n\\n/** @type {undefined|!Array} */\\nPerformanceObserverInit.prototype.entryTypes;\\n/** @type {undefined|boolean} */\\nPerformanceObserverInit.prototype.buffered;\\n\\n/**\\n * @constructor\\n */\\nfunction PerformanceObserverEntryList() {}\\n\\n/** @return {!Array} */\\nPerformanceObserverEntryList.prototype.getEntries = function() {};\\n/**\\n * @param {string} type\\n * @return {!Array}\\n */\\nPerformanceObserverEntryList.prototype.getEntriesByName = function(type) {};\\n/**\\n * @param {string} name\\n * @param {string=} opt_type\\n * @return {!Array}\\n */\\nPerformanceObserverEntryList.prototype.getEntriesByType = function(\\n name, opt_type) {};\\n\",\"externs/w3c_netinfo.js\":\"/*\\n * Copyright 2017 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Externs for the Network Information API.\\n * @externs\\n */\\n\\n/**\\n * @see http://wicg.github.io/netinfo/#-dfn-networkinformation-dfn-interface\\n * @constructor\\n */\\nfunction NetworkInformation() {}\\n\\n/** @type {ConnectionType} */\\nNetworkInformation.prototype.type;\\n\\n/** @type {EffectiveConnectionType} */\\nNetworkInformation.prototype.effectiveType;\\n\\n/** @type {Megabit} */\\nNetworkInformation.prototype.downlinkMax;\\n\\n/** @type {Megabit} */\\nNetworkInformation.prototype.downlink;\\n\\n/** @type {Millisecond} */\\nNetworkInformation.prototype.rtt;\\n\\n/** @type {?function(Event)} */\\nNetworkInformation.prototype.onchange;\\n\\n/**\\n * @typedef {number}\\n */\\nvar Megabit;\\n\\n/**\\n * @typedef {number}\\n */\\nvar Millisecond;\\n\\n/**\\n * Enum of:\\n * 'bluetooth',\\n * 'cellular',\\n * 'ethernet',\\n * 'mixed',\\n * 'none',\\n * 'other',\\n * 'unknown',\\n * 'wifi',\\n * 'wimax'\\n * @typedef {string}\\n */\\nvar ConnectionType;\\n\\n/**\\n * Enum of:\\n * '2g',\\n * '3g',\\n * '4g',\\n * 'slow-2g'\\n * @typedef {string}\\n */\\nvar EffectiveConnectionType;\\n\\n/** @type {!NetworkInformation} */\\nNavigator.prototype.connection;\\n\",\"externs/w3c_permissions.js\":\"/*\\n * Copyright 2015 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for W3C's Permissions API.\\n * @see https://w3c.github.io/permissions/\\n *\\n * @externs\\n */\\n\\n\\n/**\\n * @typedef {{name: PermissionName}}\\n * @see https://w3c.github.io/permissions/#permission-descriptor\\n */\\nvar PermissionDescriptor;\\n\\n\\n/**\\n * @typedef {{name: PermissionName, userVisibleOnly: boolean}}\\n * @see https://w3c.github.io/permissions/#push\\n */\\nvar PushPermissionDescriptor;\\n\\n\\n/**\\n * @typedef {{name: PermissionName, sysex: boolean}}\\n * @see https://w3c.github.io/permissions/#midi\\n */\\nvar MidiPermissionDescriptor;\\n\\n\\n/**\\n * Set of possible values: 'geolocation', 'notifications', 'push', 'midi'.\\n * @typedef {string}\\n * @see https://w3c.github.io/permissions/#idl-def-PermissionName\\n */\\nvar PermissionName;\\n\\n\\n/**\\n * Set of possible values: 'granted', 'denied', 'prompt'.\\n * @typedef {string}\\n * @see https://w3c.github.io/permissions/#idl-def-PermissionState\\n */\\nvar PermissionState;\\n\\n\\n/**\\n * @constructor\\n * @implements {EventTarget}\\n * @see https://w3c.github.io/permissions/#status-of-a-permission\\n */\\nfunction PermissionStatus() {}\\n\\n/** @type {PermissionState} */\\nPermissionStatus.prototype.state;\\n\\n/**\\n * @type {PermissionState}\\n * @deprecated, use PermissionStatus.state for newer clients\\n */\\nPermissionStatus.prototype.status;\\n\\n/** @type {?function(!Event)} */\\nPermissionStatus.prototype.onchange;\\n\\n/** @override */\\nPermissionStatus.prototype.addEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nPermissionStatus.prototype.removeEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nPermissionStatus.prototype.dispatchEvent = function(evt) {};\\n\\n\\n/**\\n * @constructor\\n * @see https://w3c.github.io/permissions/#idl-def-permissions\\n */\\nfunction Permissions() {}\\n\\n/**\\n * @param {PermissionDescriptor} permission The permission to look up\\n * @return {!Promise}\\n * @see https://w3c.github.io/permissions/#dom-permissions-query\\n */\\nPermissions.prototype.query = function(permission) {};\\n\\n\\n/** @type {Permissions} */\\nNavigator.prototype.permissions;\\n\",\"externs/w3c_pointer_events.js\":\"/*\\n * Copyright 2014 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for W3C's Pointer Events specification.\\n * Created from\\n * http://www.w3.org/TR/pointerevents/\\n *\\n * @externs\\n */\\n\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/pointerevents/#the-touch-action-css-property\\n */\\nCSSProperties.prototype.touchAction;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/pointerevents/#widl-Navigator-pointerEnabled\\n */\\nNavigator.prototype.pointerEnabled;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/pointerevents/#widl-Navigator-maxTouchPoints\\n */\\nNavigator.prototype.maxTouchPoints;\\n\\n\\n/**\\n * @param {number} pointerId\\n * @see https://www.w3.org/TR/pointerevents/#widl-Element-setPointerCapture-void-long-pointerId\\n */\\nElement.prototype.setPointerCapture = function(pointerId) {};\\n\\n/**\\n * @param {number} pointerId\\n * @see https://www.w3.org/TR/pointerevents/#widl-Element-releasePointerCapture-void-long-pointerId\\n */\\nElement.prototype.releasePointerCapture = function(pointerId) {};\\n\\n\\n/**\\n * @record\\n * @extends {MouseEventInit}\\n * @see https://www.w3.org/TR/pointerevents/#idl-def-PointerEventInit\\n */\\nfunction PointerEventInit() {}\\n\\n/** @type {undefined|number} */\\nPointerEventInit.prototype.pointerId;\\n\\n/** @type {undefined|number} */\\nPointerEventInit.prototype.width;\\n\\n/** @type {undefined|number} */\\nPointerEventInit.prototype.height;\\n\\n/** @type {undefined|number} */\\nPointerEventInit.prototype.pressure;\\n\\n/** @type {undefined|number} */\\nPointerEventInit.prototype.tiltX;\\n\\n/** @type {undefined|number} */\\nPointerEventInit.prototype.tiltY;\\n\\n/** @type {undefined|string} */\\nPointerEventInit.prototype.pointerType;\\n\\n/** @type {undefined|boolean} */\\nPointerEventInit.prototype.isPrimary;\\n\\n/**\\n * @constructor\\n * @extends {MouseEvent}\\n * @param {string} type\\n * @param {PointerEventInit=} opt_eventInitDict\\n * @see http://www.w3.org/TR/pointerevents/#pointerevent-interface\\n */\\nfunction PointerEvent(type, opt_eventInitDict) {}\\n\\n/** @type {number} */\\nPointerEvent.prototype.pointerId;\\n\\n/** @type {number} */\\nPointerEvent.prototype.width;\\n\\n/** @type {number} */\\nPointerEvent.prototype.height;\\n\\n/** @type {number} */\\nPointerEvent.prototype.pressure;\\n\\n/** @type {number} */\\nPointerEvent.prototype.tiltX;\\n\\n/** @type {number} */\\nPointerEvent.prototype.tiltY;\\n\\n/** @type {string} */\\nPointerEvent.prototype.pointerType;\\n\\n/** @type {boolean} */\\nPointerEvent.prototype.isPrimary;\\n\\n// Microsoft pointerType values\\n/** @type {string} */\\nPointerEvent.prototype.MSPOINTER_TYPE_TOUCH;\\n\\n/** @type {string} */\\nPointerEvent.prototype.MSPOINTER_TYPE_PEN;\\n\\n/** @type {string} */\\nPointerEvent.prototype.MSPOINTER_TYPE_MOUSE;\\n\\n/**\\n * @see https://w3c.github.io/pointerevents/extension.html\\n * @return {!Array}\\n */\\nPointerEvent.prototype.getCoalescedEvents = function() {};\\n\\n\",\"externs/w3c_range.js\":\"/*\\n * Copyright 2008 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for W3C's range specification.\\n * This file depends on w3c_dom2.js.\\n * The whole file has been fully type annotated.\\n * Created from\\n * http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html\\n *\\n * @externs\\n * @author acleung@google.com (Alan Leung)\\n * @author stevey@google.com (Steve Yegge)\\n */\\n\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Interface\\n */\\nfunction Range() {}\\n\\n// constants on the constructor\\n\\n/**\\n * @const {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-compareHow\\n */\\nRange.START_TO_START = 0;\\n\\n/**\\n * @const {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-compareHow\\n */\\nRange.START_TO_END = 1;\\n\\n/**\\n * @const {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-compareHow\\n */\\nRange.END_TO_END = 2;\\n\\n/**\\n * @const {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversa"; +a.a+='l-Range/ranges.html#Level2-Range-compareHow\\n */\\nRange.END_TO_START = 3;\\n\\n// constants repeated on the prototype\\n\\n/**\\n * @const {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-compareHow\\n */\\nRange.prototype.START_TO_START = 0;\\n\\n/**\\n * @const {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-compareHow\\n */\\nRange.prototype.START_TO_END = 1;\\n\\n/**\\n * @const {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-compareHow\\n */\\nRange.prototype.END_TO_END = 2;\\n\\n/**\\n * @const {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-compareHow\\n */\\nRange.prototype.END_TO_START = 3;\\n\\n/**\\n * @type {Node}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-attr-startParent\\n */\\nRange.prototype.startContainer;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-attr-startOffset\\n */\\nRange.prototype.startOffset;\\n\\n/**\\n * @type {Node}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-attr-endParent\\n */\\nRange.prototype.endContainer;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-attr-endOffset\\n */\\nRange.prototype.endOffset;\\n\\n/**\\n * @type {boolean}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-attr-collapsed\\n */\\nRange.prototype.collapsed;\\n\\n/**\\n * @type {Node}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-attr-commonParent\\n */\\nRange.prototype.commonAncestorContainer;\\n\\n/**\\n * @param {Node} refNode\\n * @param {number} offset\\n * @return {undefined}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-setStart\\n */\\nRange.prototype.setStart = function(refNode, offset) {};\\n\\n/**\\n * @param {Node} refNode\\n * @param {number} offset\\n * @return {undefined}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-setEnd\\n */\\nRange.prototype.setEnd = function(refNode, offset) {};\\n\\n/**\\n * @param {Node} refNode\\n * @return {undefined}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-setStartBefore\\n */\\nRange.prototype.setStartBefore = function(refNode) {};\\n\\n/**\\n * @param {Node} refNode\\n * @return {undefined}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-setStartAfter\\n */\\nRange.prototype.setStartAfter = function(refNode) {};\\n\\n/**\\n * @param {Node} refNode\\n * @return {undefined}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-setEndBefore\\n */\\nRange.prototype.setEndBefore = function(refNode) {};\\n\\n/**\\n * @param {Node} refNode\\n * @return {undefined}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-setEndAfter\\n */\\nRange.prototype.setEndAfter = function(refNode) {};\\n\\n/**\\n * @param {boolean} toStart\\n * @return {undefined}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-collapse\\n */\\nRange.prototype.collapse = function(toStart) {};\\n\\n/**\\n * @param {Node} refNode\\n * @return {undefined}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-selectNode\\n */\\nRange.prototype.selectNode = function(refNode) {};\\n\\n/**\\n * @param {Node} refNode\\n * @return {undefined}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-selectNodeContents\\n */\\nRange.prototype.selectNodeContents = function(refNode) {};\\n\\n/**\\n * @param {number} how\\n * @param {Range} sourceRange\\n * @return {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-compareBoundaryPoints\\n */\\nRange.prototype.compareBoundaryPoints = function(how, sourceRange) {};\\n\\n/**\\n * @return {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-deleteContents\\n */\\nRange.prototype.deleteContents = function() {};\\n\\n/**\\n * @return {DocumentFragment}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-extractContents\\n */\\nRange.prototype.extractContents = function() {};\\n\\n/**\\n * @return {DocumentFragment}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-cloneContents\\n */\\nRange.prototype.cloneContents = function() {};\\n\\n/**\\n * @param {Node} newNode\\n * @return {DocumentFragment}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-insertNode\\n */\\nRange.prototype.insertNode = function(newNode) {};\\n\\n/**\\n * @param {Node} newParent\\n * @return {undefined}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-surroundContents\\n */\\nRange.prototype.surroundContents = function(newParent) {};\\n\\n/**\\n * @return {Range}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-clone\\n */\\nRange.prototype.cloneRange = function() {};\\n\\n/**\\n * @return {undefined}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-detach\\n */\\nRange.prototype.detach = function() {};\\n\\n// Introduced in DOM Level 2:\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-DocumentRange-idl\\n */\\nfunction DocumentRange() {}\\n\\n/**\\n * @return {Range}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-DocumentRange-method-createRange\\n */\\nDocumentRange.prototype.createRange = function() {};\\n\\n// Introduced in DOM Level 2:\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#RangeException\\n */\\nfunction RangeException() {}\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#RangeExceptionCode\\n */\\nRangeException.prototype.code;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#RangeExceptionCode\\n */\\nRangeException.prototype.BAD_BOUNDARYPOINTS_ERR = 1;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#RangeExceptionCode\\n */\\nRangeException.prototype.INVALID_NODE_TYPE_ERR = 2;\\n","externs/w3c_referrer_policy.js":"/*\\n * Copyright 2018 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for W3C\'s referrer policy specification.\\n * @see https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-delivery\\n * @externs\\n */\\n\\n/** @type {string} */\\nHTMLAnchorElement.prototype.referrerPolicy;\\n\\n/** @type {string} */\\nHTMLAreaElement.prototype.referrerPolicy;\\n\\n/** @type {string} */\\nHTMLImageElement.prototype.referrerPolicy;\\n\\n/** @type {string} */\\nHTMLIFrameElement.prototype.referrerPolicy;\\n\\n/** @type {string} */\\nHTMLLinkElement.prototype.referrerPolicy;\\n","externs/w3c_rtc.js":"/*\\n * Copyright 2012 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for components of the WebRTC browser API.\\n * @see https://www.w3.org/TR/webrtc/\\n * @see https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-19\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API\\n * @see https://www.w3.org/TR/mediacapture-streams/\\n *\\n * @externs\\n * @author bemasc@google.com (Benjamin M. Schwartz)\\n */\\n\\n/**\\n * @typedef {string}\\n * @see {https://www.w3.org/TR/mediacapture-streams/\\n * #idl-def-MediaStreamTrackState}\\n * In WebIDL this is an enum with values \'live\', \'mute\', and \'ended\',\\n * but there is no mechanism in Closure for describing a specialization of\\n * the string type.\\n */\\nvar MediaStreamTrackState;\\n\\n/**\\n * @interface\\n */\\nfunction SourceInfo() {}\\n\\n/** @const {string} */\\nSourceInfo.prototype.kind;\\n\\n/** @const {string} */\\nSourceInfo.prototype.id;\\n\\n/** @const {?string} */\\nSourceInfo.prototype.label;\\n\\n/** @const {boolean} */\\nSourceInfo.prototype.facing;\\n\\n/**\\n * @interface\\n * @see https://w3c.github.io/mediacapture-image/#mediasettingsrange-section\\n */\\nfunction MediaSettingsRange() {}\\n\\n/**\\n * @type {number}\\n * @const\\n */\\nMediaSettingsRange.prototype.max;\\n\\n/**\\n * @type {number}\\n * @const\\n */\\nMediaSettingsRange.prototype.min;\\n\\n/**\\n * @type {number}\\n * @const\\n */\\nMediaSettingsRange.prototype.step;\\n\\n/**\\n * @interface\\n * @see https://www.w3.org/TR/mediacapture-streams/#idl-def-MediaTrackCapabilities\\n * @see https://w3c.github.io/mediacapture-image/#mediatrackcapabilities-section\\n */\\nfunction MediaTrackCapabilities() {}\\n\\n/** @type {number} */\\nMediaTrackCapabilities.prototype.width;\\n\\n/** @type {number} */\\nMediaTrackCapabilities.prototype.height;\\n\\n/** @type {number} */\\nMediaTrackCapabilities.prototype.aspectRatio;\\n\\n/** @type {number} */\\nMediaTrackCapabilities.prototype.frameRate;\\n\\n/** @type {!Array} */\\nMediaTrackCapabilities.prototype.facingMode;\\n\\n/** @type {number} */\\nMediaTrackCapabilities.prototype.volume;\\n\\n/** @type {number} */\\nMediaTrackCapabilities.prototype.sampleRate;\\n\\n/** @type {number} */\\nMediaTrackCapabilities.prototype.sampleSize;\\n\\n/** @type {!Array} */\\nMediaTrackCapabilities.prototype.echoCancellation;\\n\\n/** @type {number} */\\nMediaTrackCapabilities.prototype.latency;\\n\\n/** @type {number} */\\nMediaTrackCapabilities.prototype.channelCount;\\n\\n/** @type {string} */\\nMediaTrackCapabilities.prototype.deviceId;\\n\\n/** @type {string} */\\nMediaTrackCapabilities.prototype.groupId;\\n\\n/** @type {!Array} */\\nMediaTrackCapabilities.prototype.whiteBalanceMode;\\n\\n/** @type {!Array} */\\nMediaTrackCapabilities.prototype.exposureMode;\\n\\n/** @type {!Array} */\\nMediaTrackCapabilities.prototype.focusMode;\\n\\n/** @type {!MediaSettingsRange} */\\nMediaTrackCapabilities.prototype.exposureCompensation;\\n\\n/** @type {!MediaSettingsRange} */\\nMediaTrackCapabilities.prototype.colorTemperature\\n\\n/** @type {!MediaSettingsRange} */\\nMediaTrackCapabilities.prototype.iso\\n\\n/** @type {!MediaSettingsRange} */\\nMediaTrackCapabilities.prototype.brightness\\n\\n/** @type {!MediaSettingsRange} */\\nMediaTrackCapabilities.prototype.contrast\\n\\n/** @type {!MediaSettingsRange} */\\nMediaTrackCapabilities.prototype.saturation\\n\\n/** @type {!MediaSettingsRange} */\\nMediaTrackCapabilities.prototype.sharpness\\n\\n/** @type {!MediaSettingsRange} */\\nMediaTrackCapabilities.prototype.zoom\\n\\n/** @type {boolean} */\\nMediaTrackCapabilities.prototype.torch\\n\\n/**\\n * @interface\\n * @see https://www.w3.org/TR/mediacapture-streams/#media-track-settings\\n * @see https://w3c.github.io/mediacapture-image/#mediatracksettings-section\\n */\\nfunction MediaTrackSettings() {}\\n\\n/** @type {number} */\\nMediaTrackSettings.prototype.width;\\n\\n/** @type {number} */\\nMediaTrackSettings.prototype.height;\\n\\n/** @type {number} */\\nMediaTrackSettings.prototype.aspectRatio;\\n\\n/** @type {number} */\\nMediaTrackSettings.prototype.frameRate;\\n\\n/** @type {string} */\\nMediaTrackSettings.prototype.facingMode;\\n\\n/** @type {number} */\\nMediaTrackSettings.prototype.volume;\\n\\n/** @type {number} */\\nMediaTrackSettings.prototype.sampleRate;\\n\\n/** @type {number} */\\nMediaTrackSettings.prototype.sampleSize;\\n\\n/** @type {boolean} */\\nMediaTrackSettings.prototype.echoCancellation;\\n\\n/** @type {number} */\\nMediaTrackSettings.prototype.latency;\\n\\n/** @type {number} */\\nMediaTrackSettings.prototype.channelCount;\\n\\n/** @type {string} */\\nMediaTrackSettings.prototype.deviceId;\\n\\n/** @type {string} */\\nMediaTrackSettings.prototype.groupId;\\n\\n/** @type {string} */\\nMediaTrackSettings.prototype.whiteBalanceMode;\\n\\n/** @type {string} */\\nMediaTrackSettings.prototype.exposureMode;\\n\\n/** @type {string} */\\nMediaTrackSettings.prototype.focusMode;\\n\\n/** @type {!Array<{x: number, y: number}>} */\\nMediaTrackSettings.prototype.pointsOfInterest;\\n\\n/** @type {number} */\\nMediaTrackSettings.prototype.exposureCompensation;\\n\\n/** @type {number} */\\nMediaTrackSettings.prototype.colorTemperature\\n\\n/** @type {number} */\\nMediaTrackSettings.prototype.iso\\n\\n/** @type {number} */\\nMediaTrackSettings.prototype.brightness\\n\\n/** @type {number} */\\nMediaTrackSettings.prototype.contrast\\n\\n/** @type {number} */\\nMediaTrackSettings.prototype.saturation\\n\\n/** @type {number} */\\nMediaTrackSettings.prototype.sharpness\\n\\n/** @type {number} */\\nMediaTrackSettings.prototype.zoom\\n\\n/** @type {boolean} */\\nMediaTrackSettings.prototype.torch\\n\\n\\n/**\\n * @interface\\n * @see https://w3c.github.io/mediacapture-main/#media-track-supported-constraints\\n */\\nfunction MediaTrackSupportedConstraints() {}\\n\\n/** @type {boolean|undefined} */\\nMediaTrackSupportedConstraints.prototype.width;\\n\\n/** @type {boolean|undefined} */\\nMediaTrackSupportedConstraints.prototype.height;\\n\\n/** @type {boolean|undefined} */\\nMediaTrackSupportedConstraints.prototype.aspectRatio;\\n\\n/** @type {boolean|undefined} */\\nMediaTrackSupportedConstraints.prototype.frameRate;\\n\\n/** @type {boolean|undefined} */\\nMediaTrackSupportedConstraints.prototype.facingMode;\\n\\n/** @type {boolean|undefined} */\\nMediaTrackSupportedConstraints.prototype.volume;\\n\\n/** @type {boolean|undefined} */\\nMediaTrackSupportedConstraints.prototype.sampleRate;\\n\\n/** @type {boolean|undefined} */\\nMediaTrackSupportedConstraints.prototype.sampleSize;\\n\\n/** @type {boolean|undefined} */\\nMediaTrackSupportedConstraints.prototype.echoCancellation;\\n\\n/** @type {boolean|undefined} */\\nMediaTrackSupportedConstraints.prototype.autoGainControl;\\n\\n/** @type {boolean|undefined} */\\nMediaTrackSupportedConstraints.prototype.noiseSuppression;\\n\\n/** @type {boolean|undefined} */\\nMediaTrackSupportedConstraints.prototype.latency;\\n\\n/** @type {boolean|undefined} */\\nMediaTrackSupportedConstraints.prototype.channelCount;\\n\\n/** @type {boolean|undefined} */\\nMediaTrackSupportedConstraints.prototype.deviceId;\\n\\n/** @type {boolean|undefined} */\\nMediaTrackSupportedConstraints.prototype.groupId;\\n\\n\\n/**\\n * @interface\\n * @extends {EventTarget}\\n * @see https://www.w3.org/TR/mediacapture-streams/#mediastreamtrack\\n */\\nfunction MediaStreamTrack() {}\\n\\n/**\\n * @param {!function(!Array)} callback\\n * @return {undefined}\\n * @deprecated Use MediaDevices.enumerateDevices().\\n */\\nMediaStreamTrack.getSources = function(callback) {};\\n\\n/**\\n * @type {string}\\n * @const\\n */\\nMediaStreamTrack.prototype.kind;\\n\\n/**\\n * @type {string}\\n * @const\\n */\\nMediaStreamTrack.prototype.id;\\n\\n/**\\n * @type {string}\\n * @const\\n */\\nMediaStreamTrack.prototype.label;\\n\\n/**\\n * @type {boolean}\\n */\\nMediaStreamTrack.prototype.enabled;\\n\\n/**\\n * @type {boolean}\\n * @const\\n */\\nMediaStreamTrack.prototype.muted;\\n\\n/**\\n * @type {string}\\n * @see https://crbug.com/653531\\n * @see https://wicg.github.io/mst-content-hint/\\n */\\nMediaStreamTrack.prototype.contentHint;\\n\\n/**\\n * @type {boolean}\\n * @const\\n */\\nMediaStreamTrack.prototype.remote;\\n\\n/**\\n * @type {MediaStreamTrackState}\\n * Read only.\\n */\\nMediaStreamTrack.prototype.readyState;\\n\\n/**\\n * @type {?function(!Event)}\\n */\\nMediaStreamTra'; +a.a+='ck.prototype.onmute;\\n\\n/**\\n * @type {?function(!Event)}\\n */\\nMediaStreamTrack.prototype.onunmute;\\n\\n/**\\n * @type {?function(!Event)}\\n */\\nMediaStreamTrack.prototype.onended;\\n\\n/**\\n * @type {?function(!Event)}\\n */\\nMediaStreamTrack.prototype.onoverconstrained;\\n\\n/**\\n * Applies the specified set of constraints to the track, if any specified; or\\n * if no constraints are specified, removes all constraints from the track.\\n *\\n * @param {MediaTrackConstraints=} constraints Constraints to apply to the\\n * track.\\n * @return {!Promise} A |Promise| that is resolved when the constraints\\n * have been applied, or rejected if there was an error applying the\\n * constraints.\\n */\\nMediaStreamTrack.prototype.applyConstraints = function(constraints) {};\\n\\n/**\\n * @return {!MediaStreamTrack}\\n */\\nMediaStreamTrack.prototype.clone = function() {};\\n\\n/** @return {void} */\\nMediaStreamTrack.prototype.stop = function() {};\\n\\n/** @return {!MediaTrackCapabilities} */\\nMediaStreamTrack.prototype.getCapabilities = function() {};\\n\\n/** @return {!MediaTrackConstraints} */\\nMediaStreamTrack.prototype.getConstraints = function() {};\\n\\n/** @return {!MediaTrackSettings} */\\nMediaStreamTrack.prototype.getSettings = function() {};\\n\\n/**\\n * @typedef {{track: MediaStreamTrack}}\\n */\\nvar MediaStreamTrackEventInit;\\n\\n\\n/**\\n * @param {string} type\\n * @param {!MediaStreamTrackEventInit} eventInitDict\\n * @constructor\\n * @extends {Event}\\n * @see https://www.w3.org/TR/mediacapture-streams/#mediastreamtrackevent\\n */\\nfunction MediaStreamTrackEvent(type, eventInitDict) {}\\n\\n/**\\n * @type {!MediaStreamTrack}\\n * @const\\n */\\nMediaStreamTrackEvent.prototype.track;\\n\\n/**\\n * @param {!MediaStream|!Array=} streamOrTracks\\n * @constructor\\n * @implements {EventTarget}\\n * @see https://www.w3.org/TR/mediacapture-streams/#mediastream\\n */\\nfunction MediaStream(streamOrTracks) {}\\n\\n/**\\n * @override\\n */\\nMediaStream.prototype.addEventListener = function(type, listener,\\n opt_useCapture) {};\\n\\n/**\\n * @override\\n */\\nMediaStream.prototype.removeEventListener = function(type, listener,\\n opt_useCapture) {};\\n\\n/**\\n * @override\\n * @return {boolean}\\n */\\nMediaStream.prototype.dispatchEvent = function(evt) {};\\n\\n/**\\n * TODO(bemasc): Remove this property.\\n * @deprecated\\n * @type {string}\\n * @const\\n */\\nMediaStream.prototype.label;\\n\\n/**\\n * @type {string}\\n * @const\\n */\\nMediaStream.prototype.id;\\n\\n/**\\n * @return {!Array}\\n */\\nMediaStream.prototype.getAudioTracks = function() {};\\n\\n/**\\n * @return {!Array}\\n */\\nMediaStream.prototype.getVideoTracks = function() {};\\n\\n/**\\n * @return {!Array}\\n */\\nMediaStream.prototype.getTracks = function() {};\\n\\n/**\\n * @param {string} trackId\\n * @return {MediaStreamTrack}\\n */\\nMediaStream.prototype.getTrackById = function(trackId) {};\\n\\n/**\\n * @param {!MediaStreamTrack} track\\n * @return {undefined}\\n */\\nMediaStream.prototype.addTrack = function(track) {};\\n\\n/**\\n * @param {!MediaStreamTrack} track\\n * @return {undefined}\\n */\\nMediaStream.prototype.removeTrack = function(track) {};\\n\\n/**\\n * @return {!MediaStream}\\n */\\nMediaStream.prototype.clone = function() {};\\n\\n/**\\n * @deprecated\\n * @type {boolean}\\n */\\nMediaStream.prototype.ended;\\n\\n/**\\n * @deprecated\\n * @type {?function(!Event)}\\n */\\nMediaStream.prototype.onended;\\n\\n/**\\n * @type {boolean}\\n */\\nMediaStream.prototype.active;\\n\\n/**\\n * @type {?function(!Event)}\\n */\\nMediaStream.prototype.onactive;\\n\\n/**\\n * @type {?function(!Event)}\\n */\\nMediaStream.prototype.oninactive;\\n\\n/**\\n * @type {?function(!MediaStreamTrackEvent)}\\n */\\nMediaStream.prototype.onaddtrack;\\n\\n/**\\n * @type {?function(!MediaStreamTrackEvent)}\\n */\\nMediaStream.prototype.onremovetrack;\\n\\n/**\\n * @deprecated\\n * TODO(bemasc): Remove this method once browsers have updated to\\n * MediaStreamTrack.stop().\\n * @return {undefined}\\n */\\nMediaStream.prototype.stop = function() {};\\n\\n/**\\n * @type {function(new: MediaStream,\\n * (!MediaStream|!Array)=)}\\n */\\nvar webkitMediaStream;\\n\\n\\n/**\\n * @typedef {{tone: string}}\\n * @see https://www.w3.org/TR/webrtc/#dom-rtcdtmftonechangeeventinit\\n */\\nvar RTCDTMFToneChangeEventInit;\\n\\n\\n/**\\n * @param {string} type\\n * @param {!RTCDTMFToneChangeEventInit} eventInitDict\\n * @constructor\\n * @extends {Event}\\n * @see https://www.w3.org/TR/webrtc/#dom-rtcdtmftonechangeevent\\n */\\nfunction RTCDTMFToneChangeEvent(type, eventInitDict) {}\\n\\n/**\\n * @const {string}\\n */\\nRTCDTMFToneChangeEvent.prototype.tone;\\n\\n\\n/**\\n * @interface\\n * @see https://www.w3.org/TR/webrtc/#rtcdtmfsender\\n */\\nfunction RTCDTMFSender() {}\\n\\n/**\\n * @param {string} tones\\n * @param {number=} opt_duration\\n * @param {number=} opt_interToneGap\\n */\\nRTCDTMFSender.prototype.insertDTMF =\\n function(tones, opt_duration, opt_interToneGap) {};\\n\\n/**\\n * @type {?function(!RTCDTMFToneChangeEvent)}\\n */\\nRTCDTMFSender.prototype.ontonechange;\\n\\n/**\\n * @const {string}\\n */\\nRTCDTMFSender.prototype.toneBuffer;\\n\\n\\n/**\\n * @interface\\n * @see https://www.w3.org/TR/webrtc/#rtcrtpsender-interface\\n */\\nfunction RTCRtpSender() {}\\n\\n/**\\n * @const {!RTCDTMFSender}\\n */\\nRTCRtpSender.prototype.dtmf;\\n\\n/**\\n * @const {!MediaStreamTrack}\\n */\\nRTCRtpSender.prototype.track;\\n\\n/**\\n * @param {?MediaStreamTrack} track\\n * @return {!Promise}\\n * @see https://www.w3.org/TR/webrtc/#dom-rtcrtpsender\\n */\\nRTCRtpSender.prototype.replaceTrack = function(track) {};\\n\\n\\n/**\\n * @return {!RTCRtpSendParameters}\\n */\\nRTCRtpSender.prototype.getParameters = function() {};\\n\\n\\n/**\\n * @param {!RTCRtpSendParameters} params\\n * @return {!Promise}\\n */\\nRTCRtpSender.prototype.setParameters = function(params) {};\\n\\n\\n/**\\n * @record\\n * @see https://www.w3.org/TR/webrtc/#dom-rtcrtpsendparameters\\n */\\nfunction RTCRtpSendParameters() {}\\n\\n/**\\n * @type {string|undefined}\\n */\\nRTCRtpSendParameters.prototype.transactionId;\\n\\n/**\\n * @type {!Array}\\n */\\nRTCRtpSendParameters.prototype.encodings;\\n\\n/**\\n * Possible string values are \\"maintain-framerate\\", \\"maintain-resolution\\", and\\n * \\"balanced\\".\\n * @type {string|undefined}\\n */\\nRTCRtpSendParameters.prototype.degradationPreference;\\n\\n\\n/**\\n * @interface\\n * @see https://www.w3.org/TR/webrtc/#dom-rtcrtpcontributingsource\\n */\\nfunction RTCRtpContributingSource() {}\\n\\n/**\\n * @type {?number}\\n */\\nRTCRtpContributingSource.prototype.source;\\n\\n/**\\n * @type {?Date|number}\\n */\\nRTCRtpContributingSource.prototype.timestamp;\\n\\n\\n/**\\n * @interface\\n * @see https://www.w3.org/TR/webrtc/#rtcrtpreceiver-interface\\n */\\nfunction RTCRtpReceiver() {}\\n\\n/**\\n * @const {!MediaStreamTrack}\\n */\\nRTCRtpReceiver.prototype.track;\\n\\n/**\\n * @return {!Array}\\n */\\nRTCRtpReceiver.prototype.getContributingSources = function() {};\\n\\n/**\\n * @return {!Array}\\n */\\nRTCRtpReceiver.prototype.getSynchronizationSources = function() {};\\n\\n/**\\n * @see https://www.w3.org/TR/webrtc/#dom-rtcrtptransceiverinit\\n * @record\\n */\\nfunction RTCRtpTransceiverInit() {}\\n\\n/**\\n * The direction of the `RTCRtpTransceiver`. Defaults to \\"sendrecv\\".\\n * @type {?RTCRtpTransceiverDirection|undefined}\\n */\\nRTCRtpTransceiverInit.prototype.direction;\\n\\n/**\\n * The streams to add to the tranceiver\'s sender.\\n * @type {?Array|undefined}\\n */\\nRTCRtpTransceiverInit.prototype.streams;\\n\\n/**\\n * @type {?Array|undefined}\\n */\\nRTCRtpTransceiverInit.prototype.sendEncodings;\\n\\n/**\\n * @see https://www.w3.org/TR/webrtc/#dom-rtcrtpencodingparameters\\n * @record\\n */\\nfunction RTCRtpEncodingParameters() {}\\n\\n/**\\n * @type {?number|undefined}\\n */\\nRTCRtpEncodingParameters.prototype.codecPayloadType;\\n\\n/**\\n * Possible values are \\"disabled\\" and \\"enabled\\".\\n * @type {?string|undefined}\\n */\\nRTCRtpEncodingParameters.prototype.dtx;\\n\\n/**\\n * @type {?boolean|undefined}\\n */\\nRTCRtpEncodingParameters.prototype.active;\\n\\n/**\\n * Possible values are \\"very-low\\", \\"low\\" (default), \\"medium\\", and \\"high\\".\\n * @type {?string|undefined}\\n */\\nRTCRtpEncodingParameters.prototype.priority;\\n\\n/**\\n * Possible values are \\"very-low\\", \\"low\\" (default), \\"medium\\", and \\"high\\".\\n * @see https://w3c.github.io/webrtc-dscp-exp/\\n * @type {?string|undefined}\\n */\\nRTCRtpEncodingParameters.prototype.networkPriority;\\n\\n/**\\n * @type {?number|undefined}\\n */\\nRTCRtpEncodingParameters.prototype.ptime;\\n\\n/**\\n * @type {?number|undefined}\\n */\\nRTCRtpEncodingParameters.prototype.maxBitrate;\\n\\n/**\\n * @type {?number|undefined}\\n */\\nRTCRtpEncodingParameters.prototype.maxFramerate;\\n\\n/**\\n * @type {?string|number}\\n */\\nRTCRtpEncodingParameters.prototype.rid;\\n\\n/**\\n * @type {?number|number}\\n */\\nRTCRtpEncodingParameters.prototype.scaleResolutionDownBy;\\n\\n/**\\n * @interface\\n * @see https://www.w3.org/TR/webrtc/#rtcrtptransceiver-interface\\n */\\nfunction RTCRtpTransceiver() {}\\n\\n/**\\n * @const {?string}\\n */\\nRTCRtpTransceiver.prototype.mid;\\n\\n/**\\n * @const {boolean}\\n */\\nRTCRtpTransceiver.prototype.stopped;\\n\\n/**\\n * @type {!RTCRtpTransceiverDirection}\\n */\\nRTCRtpTransceiver.prototype.direction;\\n\\n/**\\n * @const {?RTCRtpTransceiverDirection}\\n */\\nRTCRtpTransceiver.prototype.currentDirection;\\n\\n/**\\n * @param {!RTCRtpTransceiverDirection} direction\\n */\\nRTCRtpTransceiver.prototype.setDirection = function(direction) {};\\n\\n/**\\n */\\nRTCRtpTransceiver.prototype.stop = function() {};\\n\\n/**\\n * @const {?RTCRtpSender}\\n */\\nRTCRtpTransceiver.prototype.sender;\\n\\n/**\\n * @const {?RTCRtpReceiver}\\n */\\nRTCRtpTransceiver.prototype.receiver;\\n\\n/**\\n * @see https://w3c.github.io/mediacapture-main/getusermedia.html#dom-longrange\\n * @record\\n */\\nfunction LongRange() {}\\n\\n/**\\n * @type {number|undefined}\\n */\\nLongRange.prototype.max;\\n\\n/**\\n * @type {number|undefined}\\n */\\nLongRange.prototype.min;\\n\\n/**\\n * @see https://w3c.github.io/mediacapture-main/getusermedia.html#dom-doublerange\\n * @record\\n */\\nfunction DoubleRange() {}\\n\\n/**\\n * @type {number|undefined}\\n */\\nDoubleRange.prototype.max;\\n\\n/**\\n * @type {number|undefined}\\n */\\nDoubleRange.prototype.min;\\n\\n\\n/**\\n * @see https://w3c.github.io/mediacapture-main/getusermedia.html#dom-constrainbooleanparameters\\n * @record\\n */\\nfunction ConstrainBooleanParameters() {}\\n\\n/**\\n * @type {boolean|undefined}\\n */\\nConstrainBooleanParameters.prototype.exact;\\n\\n/**\\n * @type {boolean|undefined}\\n */\\nConstrainBooleanParameters.prototype.ideal;\\n\\n/**\\n * @see https://w3c.github.io/mediacapture-main/getusermedia.html#dom-constraindomstringparameters\\n * @record\\n */\\nfunction ConstrainDOMStringParameters() {}\\n\\n/**\\n * @type {string|Array|undefined}\\n */\\nConstrainDOMStringParameters.prototype.exact;\\n\\n/**\\n * @type {string|Array|undefined}\\n */\\nConstrainDOMStringParameters.prototype.ideal;\\n\\n/**\\n * @see https://w3c.github.io/mediacapture-main/getusermedia.html#dom-constraindoublerange\\n * @record\\n * @extends {DoubleRange}\\n */\\nfunction ConstrainDoubleRange() {}\\n\\n/**\\n * @type {number|undefined}\\n */\\nConstrainDoubleRange.prototype.exact;\\n\\n/**\\n * @type {number|undefined}\\n */\\nConstrainDoubleRange.prototype.ideal;\\n\\n\\n/**\\n * @see https://w3c.github.io/mediacapture-main/getusermedia.html#dom-constrainlongrange\\n * @record\\n * @extends {LongRange}\\n */\\nfunction ConstrainLongRange() {}\\n\\n/**\\n * @type {number|undefined}\\n */\\nConstrainLongRange.prototype.exact;\\n\\n/**\\n * @type {number|undefined}\\n */\\nConstrainLongRange.prototype.ideal;\\n\\n/**\\n * @see https://w3c.github.io/mediacapture-main/getusermedia.html#dom-constrainboolean\\n * @typedef {boolean|ConstrainBooleanParameters}\\n */\\nvar ConstrainBoolean;\\n\\n/**\\n * @see https://w3c.github.io/mediacapture-main/getusermedia.html#dom-constraindomString\\n * @typedef {string|Array|ConstrainDOMStringParameters}\\n */\\nvar ConstrainDOMString;\\n\\n/**\\n * @see https://w3c.github.io/mediacapture-main/getusermedia.html#dom-constraindouble\\n * @typedef {number|ConstrainDoubleRange}\\n */\\nvar ConstrainDouble;\\n\\n/**\\n * @see https://w3c.github.io/mediacapture-main/getusermedia.html#dom-constrainlong\\n * @typedef {number|ConstrainLongRange}\\n */\\nvar ConstrainLong;\\n\\n\\n/**\\n * @see https://w3c.github.io/mediacapture-main/getusermedia.html#dom-mediatrackconstraintset\\n * @record\\n * @private\\n */\\nfunction MediaTrackConstraintSet() {}\\n\\n/**\\n * @type {ConstrainBoolean|undefined}\\n */\\nMediaTrackConstraintSet.prototype.autoGainControl;\\n\\n/**\\n * @type {ConstrainDouble|undefined}\\n */\\nMediaTrackConstraintSet.prototype.aspectRatio;\\n\\n/**\\n * @type {ConstrainLong|undefined}\\n */\\nMediaTrackConstraintSet.prototype.channelCount;\\n\\n/**\\n * @type {ConstrainDOMString|undefined}\\n */\\nMediaTrackConstraintSet.prototype.deviceId;\\n\\n/**\\n * @type {ConstrainBoolean|undefined}\\n */\\nMediaTrackConstraintSet.prototype.echoCancellation;\\n\\n/**\\n * @type {ConstrainDOMString|undefined}\\n */\\nMediaTrackConstraintSet.prototype.facingMode;\\n\\n/**\\n * @type {ConstrainDouble|undefined}\\n */\\nMediaTrackConstraintSet.prototype.frameRate;\\n\\n/**\\n * @type {ConstrainDOMString|undefined}\\n */\\nMediaTrackConstraintSet.prototype.groupId;\\n\\n/**\\n * @type {ConstrainLong|undefined}\\n */\\nMediaTrackConstraintSet.prototype.height;\\n\\n/**\\n * @type {ConstrainDouble|undefined}\\n */\\nMediaTrackConstraintSet.prototype.latency;\\n\\n/**\\n * @type {ConstrainBoolean|undefined}\\n */\\nMediaTrackConstraintSet.prototype.noiseSuppression;\\n\\n/**\\n * @type {ConstrainLong|undefined}\\n */\\nMediaTrackConstraintSet.prototype.sampleRate;\\n\\n/**\\n * @type {ConstrainLong|undefined}\\n */\\nMediaTrackConstraintSet.prototype.sampleSize;\\n\\n/**\\n * @type {ConstrainDouble|undefined}\\n */\\nMediaTrackConstraintSet.prototype.volume;\\n\\n/**\\n * @type {ConstrainLong|undefined}\\n */\\nMediaTrackConstraintSet.prototype.width;\\n\\n\\n/**\\n * @record\\n * @extends {MediaTrackConstraintSet}\\n */\\nfunction MediaTrackConstraints() {}\\n\\n/**\\n * @type {Array|undefined}\\n */\\nMediaTrackConstraints.prototype.advanced;\\n\\n/**\\n * @see https://w3c.github.io/mediacapture-main/getusermedia.html#media-track-constraints\\n * @record\\n */\\nfunction MediaStreamConstraints() {}\\n\\n/**\\n * @type {boolean|MediaTrackConstraints|undefined}\\n */\\nMediaStreamConstraints.prototype.audio;\\n\\n/**\\n * @type {boolean|MediaTrackConstraints|undefined}\\n */\\nMediaStreamConstraints.prototype.video;\\n\\n/**\\n * @see {http://dev.w3.org/2011/webrtc/editor/getusermedia.html#\\n * navigatorusermediaerror-and-navigatorusermediaerrorcallback}\\n * @interface\\n */\\nfunction NavigatorUserMediaError() {}\\n\\n/**\\n * @type {number}\\n * @deprecated Removed from the standard and some browsers.\\n * @const\\n */\\nNavigatorUserMediaError.prototype.PERMISSION_DENIED; /** 1 */\\n\\n/**\\n * @type {number}\\n * @deprecated Removed from the standard and some browsers.\\n * Read only.\\n */\\nNavigatorUserMediaError.prototype.code;\\n\\n/**\\n * @type {string}\\n * Read only.\\n */\\nNavigatorUserMediaError.prototype.name;\\n\\n/**\\n * @type {?string}\\n * Read only.\\n */\\nNavigatorUserMediaError.prototype.message;\\n\\n/**\\n * @type {?string}\\n * Read only.\\n */\\nNavigatorUserMediaError.prototype.constraintName;\\n\\n/**\\n * @param {MediaStreamConstraints} constraints A MediaStreamConstraints object.\\n * @param {function(!MediaStream)} successCallback\\n * A NavigatorUserMediaSuccessCallback function.\\n * @param {function(!NavigatorUserMediaError)=} errorCallback A\\n * NavigatorUserMediaErrorCallback function.\\n * @see http://dev.w3.org/2011/webrtc/editor/getusermedia.html\\n * @see https://www.w3.org/TR/mediacapture-streams/\\n * @return {undefined}\\n */\\nNavigator.prototype.webkitGetUserMedia =\\n function(constraints, successCallback, errorCallback) {};\\n\\n/**\\n * @param {string} type\\n * @param {!Object} eventInitDict\\n * @constructor\\n */\\nfunction MediaStreamEvent(type, eventInitDict) {}\\n\\n/**\\n * @type {?MediaStream}\\n * @const\\n */\\nMediaStreamEvent.prototype.stream;\\n\\n/**\\n * @record\\n * @see https://www.w3.org/TR/mediastream-recording/#dictdef-mediarecorderoptions\\n */\\nfunction MediaRecorderOptions() {}\\n\\n/** @type {(string|undefined)} */\\nMediaRecorderOptions.prototype.mimeType\\n\\n/** @type {(number|un'; +a.a+='defined)} */\\nMediaRecorderOptions.prototype.audioBitsPerSecond\\n\\n/** @type {(number|undefined)} */\\nMediaRecorderOptions.prototype.videoBitsPerSecond\\n\\n/** @type {(number|undefined)} */\\nMediaRecorderOptions.prototype.bitsPerSecond\\n\\n/**\\n * @see https://www.w3.org/TR/mediastream-recording/#mediarecorder-api\\n * @param {!MediaStream} stream\\n * @param {MediaRecorderOptions=} options\\n * @implements {EventTarget}\\n * @constructor\\n */\\nfunction MediaRecorder(stream, options) {}\\n\\n/**\\n * @override\\n */\\nMediaRecorder.prototype.addEventListener = function(type, listener,\\n opt_useCapture) {};\\n\\n/**\\n * @override\\n */\\nMediaRecorder.prototype.removeEventListener = function(type, listener,\\n opt_useCapture) {};\\n\\n/**\\n * @override\\n * @return {boolean}\\n */\\nMediaRecorder.prototype.dispatchEvent = function(evt) {};\\n\\n/**\\n * @type {!MediaStream}\\n */\\nMediaRecorder.prototype.stream;\\n\\n/**\\n * @type {string}\\n */\\nMediaRecorder.prototype.mimeType;\\n\\n/**\\n * @type {string}\\n */\\nMediaRecorder.prototype.state;\\n\\n/**\\n * @type {(function(!Event)|undefined)}\\n */\\nMediaRecorder.prototype.onstart;\\n\\n/**\\n * @type {(function(!Event)|undefined)}\\n */\\nMediaRecorder.prototype.onstop;\\n\\n/**\\n * @type {(function(!Event)|undefined)}\\n */\\nMediaRecorder.prototype.ondataavailable;\\n\\n/**\\n * @type {(function(!Event)|undefined)}\\n */\\nMediaRecorder.prototype.onpause;\\n\\n/**\\n * @type {(function(!Event)|undefined)}\\n */\\nMediaRecorder.prototype.onresume;\\n\\n/**\\n * @type {(function(!Event)|undefined)}\\n */\\nMediaRecorder.prototype.onerror;\\n\\n/**\\n * @type {number}\\n */\\nMediaRecorder.prototype.videoBitsPerSecond;\\n\\n/**\\n * @type {number}\\n */\\nMediaRecorder.prototype.audioBitsPerSecond;\\n\\n/**\\n * @param {number=} timeslice\\n */\\nMediaRecorder.prototype.start = function(timeslice) {};\\n\\n/** @return {void} */\\nMediaRecorder.prototype.stop = function() {};\\n\\n/** @return {void} */\\nMediaRecorder.prototype.pause = function() {};\\n\\n/** @return {void} */\\nMediaRecorder.prototype.resume = function() {};\\n\\n/** @return {void} */\\nMediaRecorder.prototype.requestData = function() {};\\n\\n/**\\n * @param {string} type\\n * @return {boolean}\\n */\\nMediaRecorder.isTypeSupported = function(type) {};\\n\\n/**\\n * @interface\\n * @see https://w3c.github.io/mediacapture-image/##photosettings-section\\n */\\nfunction PhotoSettings() {}\\n\\n/**\\n * @type {string}\\n */\\nPhotoSettings.prototype.fillLightMode;\\n\\n/**\\n * @type {number}\\n */\\nPhotoSettings.prototype.imageHeight;\\n\\n/**\\n * @type {number}\\n */\\nPhotoSettings.prototype.imageWidth;\\n\\n/**\\n * @type {boolean}\\n */\\nPhotoSettings.prototype.redEyeReduction;\\n\\n/**\\n * @interface\\n * @see https://w3c.github.io/mediacapture-image/##photocapabilities-section\\n */\\nfunction PhotoCapabilities() {}\\n\\n/**\\n * @type {string}\\n * @const\\n */\\nPhotoCapabilities.prototype.redEyeReduction;\\n\\n/**\\n * @type {!MediaSettingsRange}\\n * @const\\n */\\nPhotoCapabilities.prototype.imageHeight;\\n\\n/**\\n * @type {!MediaSettingsRange}\\n * @const\\n */\\nPhotoCapabilities.prototype.imageWidth;\\n\\n/**\\n * @type {!Array}\\n * @const\\n */\\nPhotoCapabilities.prototype.fillLightMode;\\n\\n/**\\n * @see https://w3c.github.io/mediacapture-image/\\n * @param {!MediaStreamTrack} videoTrack\\n * @constructor\\n */\\nfunction ImageCapture(videoTrack) {}\\n\\n/**\\n * @param {!PhotoSettings=} photoSettings\\n * @return {!Promise}\\n */\\nImageCapture.prototype.takePhoto = function(photoSettings) {};\\n\\n/**\\n * @return {!Promise}\\n */\\nImageCapture.prototype.getPhotoCapabilities = function() {};\\n\\n/**\\n * @return {!Promise}\\n */\\nImageCapture.prototype.grabFrame = function() {};\\n\\n/**\\n * @type {!MediaStreamTrack}\\n * @const\\n */\\nImageCapture.prototype.track;\\n\\n/**\\n * @see https://www.w3.org/TR/webrtc/#rtctrackevent\\n * @param {string} type\\n * @param {!Object} eventInitDict\\n * @constructor\\n */\\nfunction RTCTrackEvent(type, eventInitDict) {}\\n\\n/**\\n * @type {?RTCRtpReceiver}\\n * @const\\n */\\nRTCTrackEvent.prototype.receiver;\\n\\n/**\\n * @type {?MediaStreamTrack}\\n * @const\\n */\\nRTCTrackEvent.prototype.track;\\n\\n/**\\n * @type {?Array}\\n * @const\\n */\\nRTCTrackEvent.prototype.streams;\\n\\n/**\\n * @type {?RTCRtpTransceiver}\\n * @const\\n */\\nRTCTrackEvent.prototype.transceiver;\\n\\n/**\\n * @typedef {string}\\n * @see https://www.w3.org/TR/mediacapture-streams/#idl-def-MediaDeviceKind\\n * In WebIDL this is an enum with values \'audioinput\', \'audiooutput\', and\\n * \'videoinput\', but there is no mechanism in Closure for describing a\\n * specialization of the string type.\\n */\\nvar MediaDeviceKind;\\n\\n/**\\n * Possible values are \\"sendrecv\\", \\"sendonly\\", \\"recvonly\\", and \\"inactive\\".\\n * @typedef {string}\\n * @see https://www.w3.org/TR/webrtc/#dom-rtcrtptransceiverdirection\\n */\\nvar RTCRtpTransceiverDirection;\\n\\n/**\\n * @interface\\n */\\nfunction MediaDeviceInfo() {}\\n\\n/** @const {string} */\\nMediaDeviceInfo.prototype.deviceId;\\n\\n/** @const {!MediaDeviceKind} */\\nMediaDeviceInfo.prototype.kind;\\n\\n/** @const {string} */\\nMediaDeviceInfo.prototype.label;\\n\\n/** @const {string} */\\nMediaDeviceInfo.prototype.groupId;\\n\\n/**\\n * @interface\\n * @extends {EventTarget}\\n * @see https://www.w3.org/TR/mediacapture-streams/#mediadevices\\n */\\nfunction MediaDevices() {}\\n\\n/**\\n * @return {!Promise>}\\n */\\nMediaDevices.prototype.enumerateDevices = function() {};\\n\\n/**\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia\\n * @param {!MediaStreamConstraints} constraints\\n * @return {!Promise}\\n */\\nMediaDevices.prototype.getUserMedia = function(constraints) {}\\n\\n/**\\n * @see https://w3c.github.io/mediacapture-main/#dom-mediadevices-getsupportedconstraints\\n * @return {!MediaTrackSupportedConstraints}\\n */\\nMediaDevices.prototype.getSupportedConstraints = function() {}\\n\\n/** @const {!MediaDevices} */\\nNavigator.prototype.mediaDevices;\\n\\n/**\\n * @typedef {string}\\n * @see https://www.w3.org/TR/webrtc/#rtcsdptype\\n * In WebIDL this is an enum with values \'offer\', \'pranswer\', and \'answer\',\\n * but there is no mechanism in Closure for describing a specialization of\\n * the string type.\\n */\\nvar RTCSdpType;\\n\\n/**\\n * @param {!Object=} descriptionInitDict The RTCSessionDescriptionInit\\n * dictionary. This optional argument may have type\\n * {type:RTCSdpType, sdp:string}, but neither of these keys are required to be\\n * present, and other keys are ignored, so the closest Closure type is Object.\\n * @constructor\\n * @see https://www.w3.org/TR/webrtc/#rtcsessiondescription-class\\n */\\nfunction RTCSessionDescription(descriptionInitDict) {}\\n\\n/**\\n * @type {?RTCSdpType}\\n * @see https://www.w3.org/TR/webrtc/#dom-rtcsessiondescription-type\\n */\\nRTCSessionDescription.prototype.type;\\n\\n/**\\n * @type {?string}\\n * @see https://www.w3.org/TR/webrtc/#dom-rtcsessiondescription-sdp\\n */\\nRTCSessionDescription.prototype.sdp;\\n\\n/**\\n * TODO(bemasc): Remove this definition once it is removed from the browser.\\n * @param {string} label The label index (audio/video/data -> 0,1,2)\\n * @param {string} sdp The ICE candidate in SDP text form\\n * @constructor\\n */\\nfunction IceCandidate(label, sdp) {}\\n\\n/**\\n * @return {string}\\n */\\nIceCandidate.prototype.toSdp = function() {};\\n\\n/**\\n * @type {?string}\\n */\\nIceCandidate.prototype.label;\\n\\n/** @record */\\nfunction RTCIceCandidateInit() {};\\n\\n/** @type {?string|undefined} */\\nRTCIceCandidateInit.prototype.candidate;\\n\\n/** @type {(?string|undefined)} */\\nRTCIceCandidateInit.prototype.sdpMid;\\n\\n/** @type {(?number|undefined)} */\\nRTCIceCandidateInit.prototype.sdpMLineIndex;\\n\\n/** @type {(string|undefined)} */\\nRTCIceCandidateInit.prototype.usernameFragment;\\n\\n/**\\n * @param {!RTCIceCandidateInit=} candidateInitDict The RTCIceCandidateInit dictionary.\\n * @constructor\\n * @see https://www.w3.org/TR/webrtc/#rtcicecandidate-interface\\n */\\nfunction RTCIceCandidate(candidateInitDict) {}\\n\\n/**\\n * @type {?string}\\n */\\nRTCIceCandidate.prototype.candidate;\\n\\n/**\\n * @type {?string}\\n */\\nRTCIceCandidate.prototype.sdpMid;\\n\\n/**\\n * @type {?number}\\n */\\nRTCIceCandidate.prototype.sdpMLineIndex;\\n\\n/**\\n * @typedef {{urls: string}|{urls: !Array}}\\n * @private\\n * @see https://www.w3.org/TR/webrtc/#rtciceserver-dictionary\\n * This dictionary type also has an optional key {credential: ?string}.\\n */\\nvar RTCIceServerRecord_;\\n\\n/**\\n * @interface\\n * @private\\n */\\nfunction RTCIceServerInterface_() {}\\n\\n/**\\n * @type {string|!Array}\\n */\\nRTCIceServerInterface_.prototype.urls;\\n\\n/**\\n * @type {?string}\\n */\\nRTCIceServerInterface_.prototype.username;\\n\\n/**\\n * @type {?string}\\n */\\nRTCIceServerInterface_.prototype.credential;\\n\\n/**\\n * This type, and several below it, are constructed as unions between records\\n *\\n * @typedef {RTCIceServerRecord_|RTCIceServerInterface_}\\n * @private\\n */\\nvar RTCIceServer;\\n\\n/**\\n * @typedef {{\\n * iceServers: !Array,\\n * sdpSemantics: (string|undefined)\\n * }}\\n * @private\\n */\\nvar RTCConfigurationRecord_;\\n\\n/**\\n * @interface\\n * @private\\n */\\nfunction RTCConfigurationInterface_() {}\\n\\n/**\\n * @type {!Array}\\n */\\nRTCConfigurationInterface_.prototype.iceServers;\\n\\n/**\\n * Allows specifying the SDP semantics. Valid values are \\"plan-b\\" and\\n * \\"unified-plan\\".\\n *\\n * @see {@link https://webrtc.org/web-apis/chrome/unified-plan/}\\n * @type {string|undefined}\\n */\\nRTCConfigurationInterface_.prototype.sdpSemantics;\\n\\n/**\\n * @typedef {RTCConfigurationRecord_|RTCConfigurationInterface_}\\n */\\nvar RTCConfiguration;\\n\\n/**\\n * @typedef {function(!RTCSessionDescription)}\\n */\\nvar RTCSessionDescriptionCallback;\\n\\n/**\\n * @typedef {function(string)}\\n */\\nvar RTCPeerConnectionErrorCallback;\\n\\n/**\\n * @typedef {function()}\\n */\\nvar RTCVoidCallback;\\n\\n/**\\n * @typedef {string}\\n */\\nvar RTCSignalingState;\\n\\n/**\\n * @typedef {string}\\n */\\nvar RTCIceConnectionState;\\n\\n/**\\n * @typedef {string}\\n */\\nvar RTCIceGatheringState;\\n\\n/**\\n * @param {string} type\\n * @param {!Object} eventInitDict\\n * @constructor\\n */\\nfunction RTCPeerConnectionIceEvent(type, eventInitDict) {}\\n\\n/**\\n * @type {RTCIceCandidate}\\n * @const\\n */\\nRTCPeerConnectionIceEvent.prototype.candidate;\\n\\n// Note: The specification of RTCStats types is still under development.\\n// Declarations here will be updated and removed to follow the development of\\n// modern browsers, breaking compatibility with older versions as they become\\n// obsolete.\\n/**\\n * @see https://www.w3.org/TR/webrtc/#dom-rtcstats\\n * @interface\\n */\\nfunction RTCStats() {}\\n\\n/**\\n * @type {?Date|number}\\n * @const\\n */\\nRTCStats.prototype.timestamp;\\n\\n/**\\n * https://www.w3.org/TR/webrtc-stats/#rtcstatstype-str*\\n * @type {string}\\n * @const\\n */\\nRTCStats.prototype.type;\\n\\n/**\\n * @type {string}\\n * @const\\n */\\nRTCStats.prototype.id;\\n\\n/**\\n * @see https://www.w3.org/TR/webrtc-stats/#dom-rtcrtpstreamstats\\n * @interface\\n * @extends {RTCStats}\\n */\\nfunction RTCStreamStats() {}\\n\\n/** @const {number} */\\nRTCStreamStats.prototype.ssrc;\\n\\n/** @const {string} */\\nRTCStreamStats.prototype.kind;\\n\\n/** @const {string} */\\nRTCStreamStats.prototype.transportId;\\n\\n/** @const {string} */\\nRTCStreamStats.prototype.codecId;\\n\\n/** @const {number} */\\nRTCStreamStats.prototype.firCount;\\n\\n/** @const {number} */\\nRTCStreamStats.prototype.pliCount;\\n\\n/** @const {number} */\\nRTCStreamStats.prototype.nackCount;\\n\\n/** @const {number} */\\nRTCStreamStats.prototype.sliCount;\\n\\n/** @const {number} */\\nRTCStreamStats.prototype.qpSum;\\n\\n/**\\n * @interface\\n */\\nfunction RTCStatsReport() {}\\n\\n/**\\n * @type {?Date|number}\\n * @const\\n */\\nRTCStatsReport.prototype.timestamp;\\n\\n/**\\n * @return {!Array}\\n */\\nRTCStatsReport.prototype.names = function() {};\\n\\n/**\\n * @param {string} name\\n * @return {string}\\n */\\nRTCStatsReport.prototype.stat = function(name) {};\\n\\n/**\\n * @deprecated\\n * @type {RTCStatsReport}\\n * @const\\n */\\nRTCStatsReport.prototype.local;\\n\\n/**\\n * @deprecated\\n * @type {RTCStatsReport}\\n * @const\\n */\\nRTCStatsReport.prototype.remote;\\n\\n/**\\n * @type {string}\\n * @const\\n */\\nRTCStatsReport.prototype.type;\\n\\n/**\\n * @type {string}\\n * @const\\n */\\nRTCStatsReport.prototype.id;\\n\\n// Note: Below are Map like methods supported by WebRTC statistics\\n// specification-compliant RTCStatsReport. Currently only implemented by\\n// Mozilla.\\n// See https://www.w3.org/TR/webrtc/#rtcstatsreport-object for definition.\\n/**\\n * @param {function(this:SCOPE, !RTCStats, string, MAP)} callback\\n * @param {SCOPE=} opt_thisObj The value of \\"this\\" inside callback function.\\n * @this {MAP}\\n * @template MAP,SCOPE\\n * @readonly\\n */\\nRTCStatsReport.prototype.forEach = function(callback, opt_thisObj) {};\\n\\n/**\\n * @param {string} key\\n * @return {!RTCStats}\\n * @readonly\\n */\\nRTCStatsReport.prototype.get = function(key) {};\\n\\n/**\\n * @return {!IteratorIterable}\\n * @readonly\\n */\\nRTCStatsReport.prototype.keys = function() {};\\n\\n/**\\n * TODO(bemasc): Remove this type once it is no longer in use. It has already\\n * been removed from the specification.\\n * @typedef {RTCStatsReport}\\n * @deprecated\\n */\\nvar RTCStatsElement;\\n\\n/**\\n * @interface\\n */\\nfunction RTCStatsResponse() {}\\n\\n/**\\n * @return {!Array}\\n */\\nRTCStatsResponse.prototype.result = function() {};\\n\\n/**\\n * @typedef {function(!RTCStatsResponse, MediaStreamTrack=)}\\n */\\nvar RTCStatsCallback;\\n\\n/**\\n * This type is not yet standardized, so the properties here only represent\\n * the current capabilities of libjingle (and hence Chromium).\\n * TODO(bemasc): Add a link to the relevant standard once MediaConstraint has a\\n * standard definition.\\n *\\n * @interface\\n * @private\\n */\\nfunction MediaConstraintSetInterface_() {}\\n\\n/**\\n * @type {?boolean}\\n */\\nMediaConstraintSetInterface_.prototype.OfferToReceiveAudio;\\n\\n/**\\n * @type {?boolean}\\n */\\nMediaConstraintSetInterface_.prototype.OfferToReceiveVideo;\\n\\n/**\\n * @type {?boolean}\\n */\\nMediaConstraintSetInterface_.prototype.DtlsSrtpKeyAgreement;\\n\\n/**\\n * @type {?boolean}\\n */\\nMediaConstraintSetInterface_.prototype.RtpDataChannels;\\n\\n/**\\n * TODO(bemasc): Make this type public once it is defined in a standard.\\n *\\n * @typedef {Object|MediaConstraintSetInterface_}\\n * @private\\n */\\nvar MediaConstraintSet_;\\n\\n/**\\n * @interface\\n * @private\\n */\\nfunction MediaConstraintsInterface_() {}\\n\\n/**\\n * @type {?MediaConstraintSet_}\\n */\\nMediaConstraintsInterface_.prototype.mandatory;\\n\\n/**\\n * @type {?Array}\\n */\\nMediaConstraintsInterface_.prototype.optional;\\n\\n/**\\n * This type is used extensively in\\n * {@see http://dev.w3.org/2011/webrtc/editor/webrtc.html} but is not yet\\n * defined.\\n *\\n * @typedef {Object|MediaConstraintsInterface_}\\n */\\nvar MediaConstraints;\\n\\n/**\\n * @interface\\n * @extends {EventTarget}\\n */\\nfunction RTCDataChannel() {}\\n\\n/**\\n * @type {string}\\n * @const\\n */\\nRTCDataChannel.prototype.label;\\n\\n/**\\n * @type {boolean}\\n * @const\\n */\\nRTCDataChannel.prototype.reliable;\\n\\n/**\\n * An enumerated string type (RTCDataChannelState) with values:\\n * \\"connecting\\", \\"open\\", \\"closing\\", and \\"closed\\".\\n * @type {string}\\n * Read only.\\n */\\nRTCDataChannel.prototype.readyState;\\n\\n/**\\n * @type {number}\\n * Read only.\\n */\\nRTCDataChannel.prototype.bufferedAmount;\\n\\n/**\\n * @type {?function(!Event)}\\n */\\nRTCDataChannel.prototype.onopen;\\n\\n/**\\n * @type {?function(!Event)}\\n */\\nRTCDataChannel.prototype.onerror;\\n\\n/**\\n * @type {?function(!Event)}\\n */\\nRTCDataChannel.prototype.onclose;\\n\\nRTCDataChannel.prototype.close = function() {};\\n\\n/**\\n * @type {?function(!MessageEvent<*>)}\\n */\\nRTCDataChannel.prototype.onmessage;\\n\\n/**\\n * @type {string}\\n */\\nRTCDataChannel.prototype.binaryType;\\n\\n/**\\n * @param {string|!Blob|!ArrayBuffer|!ArrayBufferView} data\\n * @return {undefined}\\n */\\nRTCDataChannel.prototype.send = function(data) {};\\n\\n/**\\n * @constructor\\n * @extends {Event}\\n * @private\\n */\\nfunction RTCDataChannelEvent() {}\\n\\n/**\\n * @type {!RTCDataChannel}\\n * Read only.\\n */\\nRTCDataChannelEvent.prototype.channel;\\n\\n/**\\n * @typedef {{reliable: boolean}}\\n */\\nvar RTCDataChannelInitRecord_;\\n\\n/**\\n * @interface\\n * @private\\n */\\nfunction RTCDataChannelInitInterface_() {}\\n\\n/**\\n * @ty'; +a.a+='pe {boolean}\\n */\\nRTCDataChannelInitInterface_.prototype.reliable;\\n\\n/**\\n * @typedef {{\\n * ordered: (boolean|undefined),\\n * maxPacketLifeTime: (number|undefined),\\n * maxRetransmits: (number|undefined),\\n * protocol: (string|undefined),\\n * negotiated: (boolean|undefined),\\n * id: (number|undefined),\\n * priority: (string|undefined),\\n * }}\\n * see https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit for documentation\\n * Type inconsistencies due to Closure limitations:\\n * maxPacketLifeTime should be UnsignedShort\\n * maxRetransmits should be UnsignedShort\\n * protocol should be USVString\\n * id should be UnsignedShort\\n * In WebIDL priority is an enum with values \'very-low\', \'low\',\\n * \'medium\' and \'high\', but there is no mechanism in Closure for describing\\n * a specialization of the string type.\\n */\\nvar RTCDataChannelInitDictionary_;\\n\\n/**\\n * @typedef {RTCDataChannelInitInterface_|RTCDataChannelInitRecord_|RTCDataChannelInitDictionary_}\\n */\\nvar RTCDataChannelInit;\\n\\n/**\\n * @typedef {{expires: number}}\\n */\\nvar RTCCertificate;\\n\\n/**\\n * @param {RTCConfiguration} configuration\\n * @param {!MediaConstraints=} constraints\\n * @constructor\\n * @implements {EventTarget}\\n * @see https://www.w3.org/TR/webrtc/#interface-definition\\n */\\nfunction RTCPeerConnection(configuration, constraints) {}\\n\\n/**\\n * @param {Object} keygenAlgorithm\\n * @return {Promise}\\n */\\nRTCPeerConnection.generateCertificate = function (keygenAlgorithm) {};\\n\\n/**\\n * @override\\n */\\nRTCPeerConnection.prototype.addEventListener = function(\\n type, listener, opt_useCapture) {};\\n\\n/**\\n * @override\\n */\\nRTCPeerConnection.prototype.removeEventListener = function(\\n type, listener, opt_useCapture) {};\\n\\n/**\\n * @override\\n * @return {boolean}\\n */\\nRTCPeerConnection.prototype.dispatchEvent = function(evt) {};\\n\\n\\n// NB: Until closure annotations support overloading, many of the following\\n// functions take odd unions of parameter types. This is to support the various\\n// api differences between browsers. Generally, returning a promise means you\\n// don\'t take callback function parameters and draw any further parameters\\n// forward, and vice versa.\\n\\n/**\\n * @param {(!RTCSessionDescriptionCallback|!MediaConstraints)=}\\n * successCallbackOrConstraints\\n * @param {!RTCPeerConnectionErrorCallback=} errorCallback\\n * @param {!MediaConstraints=} constraints\\n * @return {!Promise|undefined}\\n */\\nRTCPeerConnection.prototype.createOffer = function(successCallbackOrConstraints,\\n errorCallback, constraints) {};\\n\\n/**\\n * @param {(!RTCSessionDescriptionCallback|!MediaConstraints)=}\\n * successCallbackOrConstraints\\n * @param {!RTCPeerConnectionErrorCallback=} errorCallback\\n * @param {!MediaConstraints=} constraints\\n * @return {!Promise|undefined}\\n */\\nRTCPeerConnection.prototype.createAnswer =\\n function(successCallbackOrConstraints, errorCallback, constraints) {};\\n\\n/**\\n * @param {!RTCSessionDescription} description\\n * @param {!RTCVoidCallback=} successCallback\\n * @param {!RTCPeerConnectionErrorCallback=} errorCallback\\n * @return {!Promise}\\n */\\nRTCPeerConnection.prototype.setLocalDescription = function(description,\\n successCallback, errorCallback) {};\\n\\n/**\\n * @param {!RTCSessionDescription} description\\n * @param {!RTCVoidCallback=} successCallback\\n * @param {!RTCPeerConnectionErrorCallback=} errorCallback\\n * @return {!Promise}\\n */\\nRTCPeerConnection.prototype.setRemoteDescription = function(description,\\n successCallback, errorCallback) {};\\n\\n/**\\n * @type {?RTCSessionDescription}\\n * Read only.\\n */\\nRTCPeerConnection.prototype.localDescription;\\n\\n/**\\n * @type {?RTCSessionDescription}\\n * Read only.\\n */\\nRTCPeerConnection.prototype.remoteDescription;\\n\\n/**\\n * @type {RTCSignalingState}\\n * Read only.\\n */\\nRTCPeerConnection.prototype.signalingState;\\n\\n/**\\n * @param {?RTCConfiguration=} configuration\\n * @param {?MediaConstraints=} constraints\\n * @return {undefined}\\n */\\nRTCPeerConnection.prototype.updateIce = function(configuration, constraints) {};\\n\\n/**\\n * Void in Chrome for now, a promise that you can then/catch in Firefox.\\n * @param {!RTCIceCandidate} candidate\\n * @param {!RTCVoidCallback=} successCallback\\n * @param {!function(DOMException)=} errorCallback\\n * @return {!Promise|undefined}\\n */\\nRTCPeerConnection.prototype.addIceCandidate = function(candidate, successCallback, errorCallback) {};\\n\\n/**\\n * @type {!RTCIceGatheringState}\\n * Read only.\\n */\\nRTCPeerConnection.prototype.iceGatheringState;\\n\\n/**\\n * @type {!RTCIceConnectionState}\\n * Read only.\\n */\\nRTCPeerConnection.prototype.iceConnectionState;\\n\\n/**\\n * @return {!Array}\\n */\\nRTCPeerConnection.prototype.getLocalStreams = function() {};\\n\\n/**\\n * @return {!Array}\\n */\\nRTCPeerConnection.prototype.getRemoteStreams = function() {};\\n\\n/**\\n * @param {string} streamId\\n * @return {MediaStream}\\n */\\nRTCPeerConnection.prototype.getStreamById = function(streamId) {};\\n\\n/**\\n * @return {!Array}\\n */\\nRTCPeerConnection.prototype.getSenders = function() {};\\n\\n/**\\n * @return {!Array}\\n */\\nRTCPeerConnection.prototype.getReceivers = function() {};\\n\\n/**\\n * @param {?string} label\\n * @param {RTCDataChannelInit=} dataChannelDict\\n * @return {!RTCDataChannel}\\n */\\nRTCPeerConnection.prototype.createDataChannel =\\n function(label, dataChannelDict) {};\\n/**\\n * @param {!MediaStream} stream\\n * @param {!MediaConstraints=} constraints\\n * @return {undefined}\\n */\\nRTCPeerConnection.prototype.addStream = function(stream, constraints) {};\\n\\n/**\\n * @param {!MediaStream} stream\\n * @return {undefined}\\n */\\nRTCPeerConnection.prototype.removeStream = function(stream) {};\\n\\n/**\\n * @param {!MediaStreamTrack} track\\n * @param {!MediaStream} stream\\n * @param {...MediaStream} var_args Additional streams.\\n * @return {!RTCRtpSender}\\n */\\nRTCPeerConnection.prototype.addTrack = function(track, stream, var_args) {};\\n\\n/**\\n * @param {!MediaStreamTrack|string} trackOrKind\\n * @param {?RTCRtpTransceiverInit=} init\\n * @return {!RTCRtpTransceiver}\\n */\\nRTCPeerConnection.prototype.addTransceiver = function(trackOrKind, init) {};\\n\\n/**\\n * Returns the list of transceivers are currently attached to this peer.\\n *\\n * @return {!Array}\\n */\\nRTCPeerConnection.prototype.getTransceivers = function() {};\\n\\n/**\\n * @return {!RTCConfiguration}\\n */\\nRTCPeerConnection.prototype.getConfiguration = function() {};\\n\\n/**\\n * @param {!RTCConfiguration} configuration\\n * @return {undefined}\\n */\\nRTCPeerConnection.prototype.setConfiguration = function(configuration) {};\\n\\n/**\\n * @param {!RTCRtpSender} sender\\n * @return {undefined}\\n */\\nRTCPeerConnection.prototype.removeTrack = function(sender) {};\\n\\n// TODO(bemasc): Add identity provider stuff once implementations exist\\n\\n// TODO(rjogrady): Per w3c spec, getStats() should always return a Promise.\\n// Remove RTCStatsReport from the return value once Firefox supports that.\\n/**\\n * Firefox\' getstats is synchronous and returns a much simpler\\n * {!RTCStatsReport} Map-like object.\\n * @param {!RTCStatsCallback=} successCallback\\n * @param {MediaStreamTrack=} selector\\n * @return {undefined|!RTCStatsReport|!Promise}\\n */\\nRTCPeerConnection.prototype.getStats = function(successCallback, selector) {};\\n\\nRTCPeerConnection.prototype.close = function() {};\\n\\n/**\\n * @type {?function(!Event)}\\n */\\nRTCPeerConnection.prototype.onnegotiationneeded;\\n\\n/**\\n * @type {?function(!RTCPeerConnectionIceEvent)}\\n */\\nRTCPeerConnection.prototype.onicecandidate;\\n\\n/**\\n * @type {?function(!Event)}\\n */\\nRTCPeerConnection.prototype.onicegatheringstatechange;\\n\\n/**\\n * @type {?function(!Event)}\\n */\\nRTCPeerConnection.prototype.onsignalingstatechange;\\n\\n/**\\n * @type {?function(!MediaStreamEvent)}\\n */\\nRTCPeerConnection.prototype.onaddstream;\\n\\n/**\\n * @type {?function(!RTCTrackEvent)}\\n */\\nRTCPeerConnection.prototype.ontrack;\\n\\n/**\\n * @type {?function(!MediaStreamEvent)}\\n */\\nRTCPeerConnection.prototype.onremovestream;\\n\\n/**\\n * @type {?function(!Event)}\\n */\\nRTCPeerConnection.prototype.oniceconnectionstatechange;\\n\\n/**\\n * @type {?function(!RTCDataChannelEvent)}\\n */\\nRTCPeerConnection.prototype.ondatachannel;\\n\\n/**\\n * @const\\n */\\nvar webkitRTCPeerConnection = RTCPeerConnection;\\n","externs/w3c_screen_orientation.js":"/*\\n * Copyright 2016 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for W3C\'s Screen Orientation API.\\n * @see https://w3c.github.io/screen-orientation/\\n *\\n * @externs\\n */\\n\\n/**\\n * @interface\\n * @extends {EventTarget}\\n * @see https://w3c.github.io/screen-orientation/#screenorientation-interface\\n */\\nvar ScreenOrientation = function() {};\\n\\n/**\\n * @param {string} orientation\\n * @return {!Promise}\\n */\\nScreenOrientation.prototype.lock = function(orientation) {};\\n\\n/** @return {void} */\\nScreenOrientation.prototype.unlock = function() {};\\n\\n/** @const {string} */\\nScreenOrientation.prototype.type;\\n\\n/** @const {number} */\\nScreenOrientation.prototype.angle;\\n\\n/** @type {?function(!Event)} */\\nScreenOrientation.prototype.onchange;\\n\\n/**\\n * @type {?ScreenOrientation}\\n * @see https://w3c.github.io/screen-orientation/#extensions-to-the-screen-interface\\n */\\nScreen.prototype.orientation;\\n","externs/w3c_selectors.js":"/*\\n * Copyright 2008 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for W3C\'s Selectors API.\\n * This file depends on w3c_dom1.js.\\n * @see http://www.w3.org/TR/selectors-api2/\\n *\\n * @externs\\n * @author mihaip@google.com (Mihai Parparita)\\n */\\n\\n/**\\n * @param {string} selectors\\n * @return {?Element}\\n * @override\\n * @nosideeffects\\n */\\nDocument.prototype.querySelector = function(selectors) {};\\n\\n/**\\n * @param {string} selectors\\n * @return {!NodeList}\\n * @override\\n * @nosideeffects\\n */\\nDocument.prototype.querySelectorAll = function(selectors) {};\\n\\n/**\\n * @param {string} selectors\\n * @return {?Element}\\n * @override\\n * @nosideeffects\\n */\\nElement.prototype.querySelector = function(selectors) {};\\n\\n/**\\n * @param {string} selectors\\n * @return {!NodeList}\\n * @override\\n * @nosideeffects\\n */\\nElement.prototype.querySelectorAll = function(selectors) {};\\n\\n/**\\n * https://dom.spec.whatwg.org/#dom-element-closest\\n * https://developer.mozilla.org/en-US/docs/Web/API/Element.closest\\n * @param {string} selectors\\n * @return {?Element}\\n * @nosideeffects\\n */\\nElement.prototype.closest = function(selectors) {};\\n\\n/**\\n * https://dom.spec.whatwg.org/#dom-element-matches\\n * https://developer.mozilla.org/en-US/docs/Web/API/Element.matches\\n * @param {string} selectors\\n * @return {boolean}\\n * @nosideeffects\\n */\\nElement.prototype.matches = function(selectors) {};\\n\\n/**\\n * @param {string} selectors\\n * @param {(Node|NodeList)=} refNodes\\n * @return {boolean}\\n * @nosideeffects\\n */\\nElement.prototype.matchesSelector = function(selectors, refNodes) {};\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/Node.mozMatchesSelector\\n * @param {string} selectors\\n * @return {boolean}\\n * @nosideeffects\\n */\\nElement.prototype.mozMatchesSelector = function(selectors) {};\\n\\n/**\\n * @see http://developer.apple.com/library/safari/documentation/WebKit/Reference/ElementClassRef/Element/Element.html\\n * @param {string} selectors\\n * @return {boolean}\\n * @nosideeffects\\n */\\nElement.prototype.webkitMatchesSelector = function(selectors) {};\\n\\n/**\\n * @see http://msdn.microsoft.com/en-us/library/ff975201.aspx\\n * @param {string} selectors\\n * @return {boolean}\\n * @nosideeffects\\n */\\nElement.prototype.msMatchesSelector = function(selectors) {};\\n\\n/**\\n * @see http://www.opera.com/docs/changelogs/windows/1150/\\n * @param {string} selectors\\n * @return {boolean}\\n * @nosideeffects\\n */\\nElement.prototype.oMatchesSelector = function(selectors) {};\\n","externs/w3c_serviceworker.js":"/*\\n * Copyright 2014 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n/**\\n * @fileoverview Externs for service worker.\\n *\\n * @see http://www.w3.org/TR/service-workers/\\n * @externs\\n */\\n\\n/**\\n * @see http://www.w3.org/TR/service-workers/#service-worker-interface\\n * @constructor\\n * @extends {Worker}\\n */\\nfunction ServiceWorker() {}\\n\\n/** @type {string} */\\nServiceWorker.prototype.scriptURL;\\n\\n/** @type {ServiceWorkerState} */\\nServiceWorker.prototype.state;\\n\\n/** @type {?function(!Event)} */\\nServiceWorker.prototype.onstatechange;\\n\\n/**\\n * Set of possible string values: \'installing\', \'installed\', \'activating\',\\n * \'activated\', \'redundant\'.\\n * @typedef {string}\\n */\\nvar ServiceWorkerState;\\n\\n/**\\n * @see https://w3c.github.io/ServiceWorker/#navigationpreloadmanager\\n * @constructor\\n */\\nfunction NavigationPreloadManager() {}\\n\\n/** @return {!Promise} */\\nNavigationPreloadManager.prototype.enable = function() {};\\n\\n/** @return {!Promise} */\\nNavigationPreloadManager.prototype.disable = function() {};\\n\\n/**\\n * @param {string=} value\\n * @return {!Promise}\\n */\\nNavigationPreloadManager.prototype.setHeaderValue = function(value) {};\\n\\n/** @return {!Promise} */\\nNavigationPreloadManager.prototype.getState = function() {};\\n\\n/**\\n * @typedef {{\\n * enabled: (boolean|undefined),\\n * headerValue: (string|undefined)\\n * }}\\n */\\nvar NavigationPreloadState;\\n\\n/** @record */\\nfunction PushSubscriptionOptions() {}\\n\\n/** @type {ArrayBuffer|undefined} */\\nPushSubscriptionOptions.prototype.applicationServerKey;\\n\\n/** @type {boolean|undefined} */\\nPushSubscriptionOptions.prototype.userVisibleOnly;\\n\\n/** @record */\\nfunction PushSubscriptionOptionsInit() {}\\n\\n/** @type {BufferSource|string|undefined} */\\nPushSubscriptionOptionsInit.prototype.applicationServerKey;\\n\\n/** @type {boolean|undefined} */\\nPushSubscriptionOptionsInit.prototype.userVisibleOnly;\\n\\n\\n/**\\n * @see https://w3c.github.io/push-api/\\n * @constructor\\n */\\nfunction PushSubscription() {}\\n\\n/** @type {string} */\\nPushSubscription.prototype.endpoint;\\n\\n/**\\n * Please note there is an intent to deprecate this field in Chrome 43 or 44.\\n * See https://www.chromestatus.com/feature/5283829761703936.\\n * @type {string}\\n */\\nPushSubscription.prototype.subscriptionId;\\n\\n/** @type {!PushSubscriptionOptions} */\\nPushSubscription.prototype.options;\\n\\n/** @return {!Promise} */\\nPushSubscription.prototype.unsubscribe = function() {};\\n\\n/** @enum {string} */\\n// This is commented out since it has not been implemented yet'; +a.a+=' in Chrome beta.\\n// Uncomment once it is available.\\n// var PushPermissionStatus = {\\n// GRANTED: \'granted\',\\n// DENIED: \'denied\',\\n// DEFAULT: \'default\'\\n//};\\n\\n/**\\n * @see https://w3c.github.io/push-api/#idl-def-PushManager\\n * @constructor\\n */\\nfunction PushManager() {}\\n\\n/**\\n * @param {PushSubscriptionOptionsInit=} opt_options\\n * @return {!Promise}\\n */\\nPushManager.prototype.subscribe = function(opt_options) {};\\n\\n/** @return {!Promise} */\\nPushManager.prototype.getSubscription = function() {};\\n\\n/** @return {!Promise} */\\n// This is commented out since it has not been implemented yet in Chrome beta.\\n// Uncomment once it is available.\\n// PushManager.prototype.hasPermission = function() {};\\n\\n/**\\n * @see https://wicg.github.io/BackgroundSync/spec/#sync-manager-interface\\n * @constructor\\n */\\nfunction SyncManager() {}\\n\\n/**\\n * @param {string} tag\\n * @return {!Promise}\\n */\\nSyncManager.prototype.register = function(tag) {}\\n\\n/**\\n * @return {!Promise>}\\n */\\nSyncManager.prototype.getTags = function() {}\\n\\n/**\\n * @see https://wicg.github.io/BackgroundSync/spec/#sync-event\\n * @constructor\\n * @extends{ExtendableEvent}\\n */\\nfunction SyncEvent() {}\\n\\n/** @type {string} */\\nSyncEvent.prototype.tag;\\n\\n/** @type {boolean} */\\nSyncEvent.prototype.lastChance;\\n\\n/**\\n * @see http://www.w3.org/TR/push-api/#idl-def-PushMessageData\\n * @constructor\\n */\\nfunction PushMessageData() {}\\n\\n/** @return {!ArrayBuffer} */\\nPushMessageData.prototype.arrayBuffer = function() {};\\n\\n/** @return {!Blob} */\\nPushMessageData.prototype.blob = function() {};\\n\\n/** @return {*} */\\nPushMessageData.prototype.json = function() {};\\n\\n/** @return {string} */\\nPushMessageData.prototype.text = function() {};\\n\\n\\n/**\\n * @see http://www.w3.org/TR/push-api/#idl-def-PushEvent\\n * @constructor\\n * @param {string} type\\n * @param {!ExtendableEventInit=} opt_eventInitDict\\n * @extends {ExtendableEvent}\\n */\\nfunction PushEvent(type, opt_eventInitDict) {}\\n\\n/** @type {?PushMessageData} */\\nPushEvent.prototype.data;\\n\\n\\n/**\\n * @see http://www.w3.org/TR/service-workers/#service-worker-registration-interface\\n * @interface\\n * @extends {EventTarget}\\n */\\nfunction ServiceWorkerRegistration() {}\\n\\n/** @type {ServiceWorker} */\\nServiceWorkerRegistration.prototype.installing;\\n\\n/** @type {ServiceWorker} */\\nServiceWorkerRegistration.prototype.waiting;\\n\\n/** @type {ServiceWorker} */\\nServiceWorkerRegistration.prototype.active;\\n\\n/** @type {NavigationPreloadManager} */\\nServiceWorkerRegistration.prototype.navigationPreload;\\n\\n/** @type {string} */\\nServiceWorkerRegistration.prototype.scope;\\n\\n/** @return {!Promise} */\\nServiceWorkerRegistration.prototype.unregister = function() {};\\n\\n/** @type {?function(!Event)} */\\nServiceWorkerRegistration.prototype.onupdatefound;\\n\\n/** @return {!Promise} */\\nServiceWorkerRegistration.prototype.update = function() {};\\n\\n/**\\n * @see https://w3c.github.io/push-api/\\n * @type {!PushManager}\\n */\\nServiceWorkerRegistration.prototype.pushManager;\\n\\n/**\\n * @see https://notifications.spec.whatwg.org/#service-worker-api\\n * @param {string} title\\n * @param {NotificationOptions=} opt_options\\n * @return {!Promise}\\n */\\nServiceWorkerRegistration.prototype.showNotification =\\n function(title, opt_options) {};\\n\\n/**\\n * @see https://notifications.spec.whatwg.org/#service-worker-api\\n * @param {!GetNotificationOptions=} opt_filter\\n * @return {!Promise>}\\n */\\nServiceWorkerRegistration.prototype.getNotifications = function(opt_filter) {};\\n\\n/**\\n * @see https://wicg.github.io/BackgroundSync/spec/#service-worker-registration-extensions\\n * @type {!SyncManager}\\n */\\nServiceWorkerRegistration.prototype.sync;\\n\\n/**\\n * @see http://www.w3.org/TR/service-workers/#service-worker-container-interface\\n * @interface\\n * @extends {EventTarget}\\n */\\nfunction ServiceWorkerContainer() {}\\n\\n/** @type {?ServiceWorker} */\\nServiceWorkerContainer.prototype.controller;\\n\\n/** @type {!Promise} */\\nServiceWorkerContainer.prototype.ready;\\n\\n/**\\n * @param {string} scriptURL\\n * @param {RegistrationOptions=} opt_options\\n * @return {!Promise}\\n */\\nServiceWorkerContainer.prototype.register = function(scriptURL, opt_options) {};\\n\\n/**\\n * @param {string=} opt_documentURL\\n * @return {!Promise}\\n */\\nServiceWorkerContainer.prototype.getRegistration = function(opt_documentURL) {};\\n\\n/**\\n * @return {!Promise>}\\n */\\nServiceWorkerContainer.prototype.getRegistrations = function() {};\\n\\n/** @type {?function(!Event)} */\\nServiceWorkerContainer.prototype.oncontrollerchange;\\n\\n/** @type {?function(!ErrorEvent)} */\\nServiceWorkerContainer.prototype.onerror;\\n\\n/**\\n * @typedef {{scope: (string|undefined), useCache: (boolean|undefined), updateViaCache: (string|undefined)}}\\n */\\nvar RegistrationOptions;\\n\\n/** @type {!ServiceWorkerContainer} */\\nNavigator.prototype.serviceWorker;\\n\\n/**\\n * @see http://www.w3.org/TR/service-workers/#service-worker-global-scope-interface\\n * @interface\\n * @extends {WorkerGlobalScope}\\n */\\nfunction ServiceWorkerGlobalScope() {}\\n\\n/** @type {!Cache} */\\nServiceWorkerGlobalScope.prototype.scriptCache;\\n\\n/** @type {!CacheStorage} */\\nServiceWorkerGlobalScope.prototype.caches;\\n\\n/** @type {!ServiceWorkerClients} */\\nServiceWorkerGlobalScope.prototype.clients;\\n\\n/** @type {string} */\\nServiceWorkerGlobalScope.prototype.scope;\\n\\n/** @type {!ServiceWorkerRegistration} */\\nServiceWorkerGlobalScope.prototype.registration;\\n\\n/** @return {!Promise} */\\nServiceWorkerGlobalScope.prototype.skipWaiting = function() {};\\n\\n/** @type {!Console} */\\nServiceWorkerGlobalScope.prototype.console;\\n\\n/** @type {?function(!InstallEvent)} */\\nServiceWorkerGlobalScope.prototype.oninstall;\\n\\n/** @type {?function(!ExtendableEvent)} */\\nServiceWorkerGlobalScope.prototype.onactivate;\\n\\n/** @type {?function(!FetchEvent)} */\\nServiceWorkerGlobalScope.prototype.onfetch;\\n\\n/**\\n * TODO(mtragut): This handler should get a custom event in the future.\\n * @type {?function(!Event)}\\n */\\nServiceWorkerGlobalScope.prototype.onbeforeevicted;\\n\\n/**\\n * TODO(mtragut): This handler should get a custom event in the future.\\n * @type {?function(!Event)}\\n */\\nServiceWorkerGlobalScope.prototype.onevicted;\\n\\n/** @type {?function(!MessageEvent)} */\\nServiceWorkerGlobalScope.prototype.onmessage;\\n\\n/** @type {!IDBFactory|undefined} */\\nServiceWorkerGlobalScope.prototype.indexedDB;\\n\\n/**\\n * While not strictly correct, this should be effectively correct. Notification\\n * is the Notification constructor but calling it from the Service Worker throws\\n * (https://notifications.spec.whatwg.org/#constructors) so its only use is as\\n * an object holding some static properties (note that requestPermission is only\\n * exposed to window context - https://notifications.spec.whatwg.org/#api).\\n *\\n * @type {{\\n * permission: string,\\n * maxActions: number,\\n * }}\\n */\\nServiceWorkerGlobalScope.prototype.Notification;\\n\\n/**\\n * @see http://www.w3.org/TR/service-workers/#service-worker-client-interface\\n * @constructor\\n */\\nfunction ServiceWorkerClient() {}\\n\\n/** @type {!Promise} */\\nServiceWorkerClient.prototype.ready;\\n\\n/** @type {boolean} */\\nServiceWorkerClient.prototype.hidden;\\n\\n/** @type {boolean} */\\nServiceWorkerClient.prototype.focused;\\n\\n/** @type {VisibilityState} */\\nServiceWorkerClient.prototype.visibilityState;\\n\\n/** @type {string} */\\nServiceWorkerClient.prototype.url;\\n\\n/** @type {string} */\\nServiceWorkerClient.prototype.id;\\n\\n/**\\n * // TODO(mtragut): Possibly replace the type with enum ContextFrameType once\\n * the enum is defined.\\n * @type {string}\\n */\\nServiceWorkerClient.prototype.frameType;\\n\\n/**\\n * @param {*} message\\n * @param {(!Array|undefined)=} opt_transfer\\n * @return {undefined}\\n */\\nServiceWorkerClient.prototype.postMessage = function(message, opt_transfer) {};\\n\\n/** @return {!Promise} */\\nServiceWorkerClient.prototype.focus = function() {};\\n\\n/**\\n * @param {string} url\\n * @return {!Promise}\\n */\\nServiceWorkerClient.prototype.navigate = function(url) {};\\n\\n/**\\n * @see http://www.w3.org/TR/service-workers/#service-worker-clients-interface\\n * @interface\\n */\\nfunction ServiceWorkerClients() {}\\n\\n/**\\n * Deprecated in Chrome M43+, use matchAll instead. Reference:\\n * https://github.com/slightlyoff/ServiceWorker/issues/610.\\n * TODO(joeltine): Remove when getAll is fully deprecated.\\n * @param {ServiceWorkerClientQueryOptions=} opt_options\\n * @return {!Promise>}\\n */\\nServiceWorkerClients.prototype.getAll = function(opt_options) {};\\n\\n/**\\n * @param {ServiceWorkerClientQueryOptions=} opt_options\\n * @return {!Promise>}\\n */\\nServiceWorkerClients.prototype.matchAll = function(opt_options) {};\\n\\n/**\\n * @return {!Promise}\\n */\\nServiceWorkerClients.prototype.claim = function() {};\\n\\n/**\\n * @param {string} url\\n * @return {!Promise}\\n */\\nServiceWorkerClients.prototype.openWindow = function(url) {};\\n\\n/**\\n * @param {string} id\\n * @return {!Promise}\\n */\\nServiceWorkerClients.prototype.get = function(id) {};\\n\\n/** @typedef {{includeUncontrolled: (boolean|undefined)}} */\\nvar ServiceWorkerClientQueryOptions;\\n\\n/**\\n * @see http://www.w3.org/TR/service-workers/#cache-interface\\n * @interface\\n */\\nfunction Cache() {}\\n\\n/**\\n * @param {!RequestInfo} request\\n * @param {CacheQueryOptions=} opt_options\\n * @return {!Promise}\\n */\\nCache.prototype.match = function(request, opt_options) {};\\n\\n/**\\n * @param {RequestInfo=} opt_request\\n * @param {CacheQueryOptions=} opt_options\\n * @return {!Promise>}\\n */\\nCache.prototype.matchAll = function(opt_request, opt_options) {};\\n\\n/**\\n * @param {!RequestInfo} request\\n * @return {!Promise}\\n */\\nCache.prototype.add = function(request) {};\\n\\n/**\\n * @param {!Array} requests\\n * @return {!Promise}\\n */\\nCache.prototype.addAll = function(requests) {};\\n\\n/**\\n * @param {!RequestInfo} request\\n * @param {!Response} response\\n * @return {!Promise}\\n */\\nCache.prototype.put = function(request, response) {};\\n\\n/**\\n * @param {!RequestInfo} request\\n * @param {CacheQueryOptions=} opt_options\\n * @return {!Promise}\\n */\\nCache.prototype.delete = function(request, opt_options) {};\\n\\n/**\\n * @param {RequestInfo=} opt_request\\n * @param {CacheQueryOptions=} opt_options\\n * @return {!Promise>}\\n */\\nCache.prototype.keys = function(opt_request, opt_options) {};\\n\\n/**\\n * @typedef {{\\n * ignoreSearch: (boolean|undefined),\\n * ignoreMethod: (boolean|undefined),\\n * ignoreVary: (boolean|undefined),\\n * prefixMatch: (boolean|undefined),\\n * cacheName: (string|undefined)\\n * }}\\n */\\nvar CacheQueryOptions;\\n\\n/**\\n * @see http://www.w3.org/TR/service-workers/#cache-storage-interface\\n * @interface\\n */\\nfunction CacheStorage() {}\\n\\n/**\\n * Window instances have a property called caches which implements CacheStorage\\n * @see https://www.w3.org/TR/service-workers/#cache-objects\\n * @type {!CacheStorage}\\n */\\nWindow.prototype.caches;\\n\\n/**\\n * @param {!RequestInfo} request\\n * @param {CacheQueryOptions=} opt_options\\n * @return {!Promise}\\n */\\nCacheStorage.prototype.match = function(request, opt_options) {};\\n\\n/**\\n * @param {string} cacheName\\n * @return {!Promise}\\n */\\nCacheStorage.prototype.has = function(cacheName) {};\\n\\n/**\\n * @param {string} cacheName\\n * @return {!Promise}\\n */\\nCacheStorage.prototype.open = function(cacheName) {};\\n\\n/**\\n * @param {string} cacheName\\n * @return {!Promise}\\n */\\nCacheStorage.prototype.delete = function(cacheName) {};\\n\\n/** @return {!Promise>} */\\nCacheStorage.prototype.keys = function() {};\\n\\n/**\\n * @see http://www.w3.org/TR/service-workers/#extendable-event-interface\\n * @constructor\\n * @param {string} type\\n * @param {ExtendableEventInit=} opt_eventInitDict\\n * @extends {Event}\\n */\\nfunction ExtendableEvent(type, opt_eventInitDict) {}\\n\\n/**\\n * @param {IThenable} f\\n * @return {undefined}\\n */\\nExtendableEvent.prototype.waitUntil = function(f) {};\\n\\n/**\\n * @typedef {{\\n * bubbles: (boolean|undefined),\\n * cancelable: (boolean|undefined)\\n * }}\\n */\\nvar ExtendableEventInit;\\n\\n/**\\n * @see http://www.w3.org/TR/service-workers/#install-event-interface\\n * @constructor\\n * @param {string} type\\n * @param {InstallEventInit=} opt_eventInitDict\\n * @extends {ExtendableEvent}\\n */\\nfunction InstallEvent(type, opt_eventInitDict) {}\\n\\n/** @type {ServiceWorker} */\\nExtendableEvent.prototype.activeWorker;\\n\\n/**\\n * @typedef {{\\n * bubbles: (boolean|undefined),\\n * cancelable: (boolean|undefined),\\n * activeWorker: (!ServiceWorker|undefined)\\n * }}\\n */\\nvar InstallEventInit;\\n\\n/**\\n * @see http://www.w3.org/TR/service-workers/#fetch-event-interface\\n * @constructor\\n * @param {string} type\\n * @param {FetchEventInit=} opt_eventInitDict\\n * @extends {ExtendableEvent}\\n */\\nfunction FetchEvent(type, opt_eventInitDict) {}\\n\\n/** @type {!Request} */\\nFetchEvent.prototype.request;\\n\\n/**\\n * @type {!Promise}\\n */\\nFetchEvent.prototype.preloadResponse;\\n\\n/**\\n * @type {!ServiceWorkerClient}\\n * @deprecated\\n */\\nFetchEvent.prototype.client;\\n\\n/** @type {?string} */\\nFetchEvent.prototype.clientId;\\n\\n/** @type {!boolean} */\\nFetchEvent.prototype.isReload;\\n\\n/** @type {?string} */\\nFetchEvent.prototype.resultingClientId;\\n\\n/**\\n * @param {(Response|IThenable)} r\\n * @return {undefined}\\n */\\nFetchEvent.prototype.respondWith = function(r) {};\\n\\n/**\\n * @param {string} url\\n * @return {!Promise}\\n */\\nFetchEvent.prototype.forwardTo = function(url) {};\\n\\n/**\\n * @return {!Promise}\\n */\\nFetchEvent.prototype.default = function() {};\\n\\n/**\\n * @typedef {{\\n * bubbles: (boolean|undefined),\\n * cancelable: (boolean|undefined),\\n * request: (!Request|undefined),\\n * preloadResponse: (!Promise),\\n * client: (!ServiceWorkerClient|undefined),\\n * isReload: (!boolean|undefined)\\n * }}\\n */\\nvar FetchEventInit;\\n\\n\\n/**\\n * @see https://www.w3.org/TR/service-workers/#extendablemessage-event-interface\\n * @param {string} type\\n * @param {!ExtendableMessageEventInit=} opt_eventInitDict\\n * @constructor\\n * @extends {ExtendableEvent}\\n * @template T\\n */\\nfunction ExtendableMessageEvent(type, opt_eventInitDict) {};\\n\\n/** @type {T} */\\nExtendableMessageEvent.prototype.data;\\n\\n/** @type {string} */\\nExtendableMessageEvent.prototype.origin;\\n\\n/** @type {string} */\\nExtendableMessageEvent.prototype.lastEventId;\\n\\n/** @type {?ServiceWorkerClient|?ServiceWorker|?MessagePort} */\\nExtendableMessageEvent.prototype.source;\\n\\n/** @type {?Array} */\\nExtendableMessageEvent.prototype.ports;\\n\\n\\n/**\\n * @see https://www.w3.org/TR/service-workers/#extendablemessage-event-init-dictionary\\n * @record\\n * @extends {ExtendableEventInit}\\n * @template T\\n */\\nfunction ExtendableMessageEventInit() {};\\n\\n/** @type {T} */\\nExtendableMessageEventInit.prototype.data;\\n\\n/** @type {string|undefined} */\\nExtendableMessageEventInit.prototype.origin;\\n\\n/** @type {string|undefined} */\\nExtendableMessageEventInit.prototype.lastEventId;\\n\\n/** @type {!ServiceWorkerClient|!ServiceWorker|!MessagePort|undefined} */\\nExtendableMessageEventInit.prototype.source;\\n\\n/** @type {!Array|undefined} */\\nExtendableMessageEventInit.prototype.ports;\\n","externs/w3c_speech.js":"/*\\n * Copyright 2011 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific l'; +a.a+='anguage governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for W3C\'s Speech Input 2010 draft API and the\\n * 2012 Web Speech draft API (in progress).\\n * 2010 Speech Input API:\\n * http://www.w3.org/2005/Incubator/htmlspeech/2010/10/google-api-draft.html\\n * 2012 Web Speech API:\\n * http://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html\\n * This file contains only those functions/properties that are actively\\n * used in the Voice Search experiment. Because the draft is under discussion\\n * and constantly evolving, this file does not attempt to stay in sync with it.\\n *\\n * @externs\\n * @author manas@google.com (Manas Tungare)\\n */\\n\\n// W3C Speech Input API implemented in Chrome M12\\n/**\\n * @constructor\\n * @extends {UIEvent}\\n */\\nfunction SpeechInputEvent() {}\\n\\n/** @type {SpeechInputResultList} */\\nSpeechInputEvent.prototype.results;\\n\\n\\n/**\\n * @constructor\\n */\\nfunction SpeechInputResultList() {}\\n\\n/** @type {number} */\\nSpeechInputResultList.prototype.length;\\n\\n\\n/**\\n * @constructor\\n */\\nfunction SpeechInputResult() {}\\n\\n/** @type {string} */\\nSpeechInputResult.prototype.utterance;\\n\\n/** @type {number} */\\nSpeechInputResult.prototype.confidence;\\n\\n\\n// HTMLInputElement\\n/** @type {boolean} */\\nHTMLInputElement.prototype.webkitspeech;\\n\\n/** @type {?function (Event)} */\\nHTMLInputElement.prototype.onwebkitspeechchange;\\n\\n\\n\\n// W3C Web Speech API implemented in Chrome M23\\n/**\\n * @constructor\\n * @implements {EventTarget}\\n */\\nfunction SpeechRecognition() {}\\n\\n/** @override */\\nSpeechRecognition.prototype.addEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nSpeechRecognition.prototype.removeEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nSpeechRecognition.prototype.dispatchEvent = function(evt) {};\\n\\n/** @type {SpeechGrammarList} */\\nSpeechRecognition.prototype.grammars;\\n\\n/** @type {string} */\\nSpeechRecognition.prototype.lang;\\n\\n/** @type {boolean} */\\nSpeechRecognition.prototype.continuous;\\n\\n/** @type {boolean} */\\nSpeechRecognition.prototype.interimResults;\\n\\n/** @type {number} */\\nSpeechRecognition.prototype.maxAlternatives;\\n\\n/** @type {string} */\\nSpeechRecognition.prototype.serviceURI;\\n\\n/** @type {function()} */\\nSpeechRecognition.prototype.start;\\n\\n/** @type {function()} */\\nSpeechRecognition.prototype.stop;\\n\\n/** @type {function()} */\\nSpeechRecognition.prototype.abort;\\n\\n/** @type {?function(!Event)} */\\nSpeechRecognition.prototype.onaudiostart;\\n\\n/** @type {?function(!Event)} */\\nSpeechRecognition.prototype.onsoundstart;\\n\\n/** @type {?function(!Event)} */\\nSpeechRecognition.prototype.onspeechstart;\\n\\n/** @type {?function(!Event)} */\\nSpeechRecognition.prototype.onspeechend;\\n\\n/** @type {?function(!Event)} */\\nSpeechRecognition.prototype.onsoundend;\\n\\n/** @type {?function(!Event)} */\\nSpeechRecognition.prototype.onaudioend;\\n\\n/** @type {?function(!SpeechRecognitionEvent)} */\\nSpeechRecognition.prototype.onresult;\\n\\n/** @type {?function(!SpeechRecognitionEvent)} */\\nSpeechRecognition.prototype.onnomatch;\\n\\n/** @type {?function(!SpeechRecognitionError)} */\\nSpeechRecognition.prototype.onerror;\\n\\n/** @type {?function(!Event)} */\\nSpeechRecognition.prototype.onstart;\\n\\n/** @type {?function(!Event)} */\\nSpeechRecognition.prototype.onend;\\n\\n\\n/**\\n * @constructor\\n * @extends {Event}\\n */\\nfunction SpeechRecognitionError() {}\\n\\n/** @type {string} */\\nSpeechRecognitionError.prototype.error;\\n\\n/** @type {string} */\\nSpeechRecognitionError.prototype.message;\\n\\n\\n/**\\n * @constructor\\n */\\nfunction SpeechRecognitionAlternative() {}\\n\\n/** @type {string} */\\nSpeechRecognitionAlternative.prototype.transcript;\\n\\n/** @type {number} */\\nSpeechRecognitionAlternative.prototype.confidence;\\n\\n\\n/**\\n * @constructor\\n */\\nfunction SpeechRecognitionResult() {}\\n\\n/**\\n * @type {number}\\n */\\nSpeechRecognitionResult.prototype.length;\\n\\n/**\\n * @type {function(number): SpeechRecognitionAlternative}\\n */\\nSpeechRecognitionResult.prototype.item = function(index) {};\\n\\n/**\\n * @type {boolean}\\n */\\nSpeechRecognitionResult.prototype.isFinal;\\n\\n\\n/**\\n * @constructor\\n */\\nfunction SpeechRecognitionResultList() {}\\n\\n/**\\n * @type {number}\\n */\\nSpeechRecognitionResultList.prototype.length;\\n\\n/**\\n * @type {function(number): SpeechRecognitionResult}\\n */\\nSpeechRecognitionResultList.prototype.item = function(index) {};\\n\\n\\n/**\\n * @constructor\\n * @extends {Event}\\n */\\nfunction SpeechRecognitionEvent() {}\\n\\n/** @type {number} */\\nSpeechRecognitionEvent.prototype.resultIndex;\\n\\n/** @type {SpeechRecognitionResultList} */\\nSpeechRecognitionEvent.prototype.results;\\n\\n/** @type {*} */\\nSpeechRecognitionEvent.prototype.interpretation;\\n\\n/** @type {Document} */\\nSpeechRecognitionEvent.prototype.emma;\\n\\n\\n/**\\n * @constructor\\n */\\nfunction SpeechGrammar() {}\\n\\n/** @type {string} */\\nSpeechGrammar.prototype.src;\\n\\n/** @type {number} */\\nSpeechGrammar.prototype.weight;\\n\\n\\n/**\\n * @constructor\\n */\\nfunction SpeechGrammarList() {}\\n\\n/**\\n * @type {number}\\n */\\nSpeechGrammarList.prototype.length;\\n\\n/**\\n * @type {function(number): SpeechGrammar}\\n */\\nSpeechGrammarList.prototype.item = function(index) {};\\n\\n/**\\n * @type {function(string, number)}\\n */\\nSpeechGrammarList.prototype.addFromUri = function(src, weight) {};\\n\\n/**\\n * @type {function(string, number)}\\n */\\nSpeechGrammarList.prototype.addFromString = function(str, weight) {};\\n\\n\\n// Webkit implementations of Web Speech API\\n/**\\n * @constructor\\n * @extends {SpeechGrammarList}\\n */\\nfunction webkitSpeechGrammarList() {}\\n\\n\\n/**\\n * @constructor\\n * @extends {SpeechGrammar}\\n */\\nfunction webkitSpeechGrammar() {}\\n\\n\\n/**\\n * @constructor\\n * @extends {SpeechRecognitionEvent}\\n */\\nfunction webkitSpeechRecognitionEvent() {}\\n\\n\\n/**\\n * @constructor\\n * @extends {SpeechRecognitionError}\\n */\\nfunction webkitSpeechRecognitionError() {}\\n\\n\\n/**\\n * @constructor\\n * @extends {SpeechRecognition}\\n */\\nfunction webkitSpeechRecognition() {}\\n\\n\\n\\n// W3C Web Speech Synthesis API is implemented in Chrome M33\\n/**\\n * @type {SpeechSynthesis}\\n * @see https://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html#tts-section\\n */\\nvar speechSynthesis;\\n\\n\\n/**\\n * @constructor\\n * @param {string} text\\n */\\nfunction SpeechSynthesisUtterance(text) {}\\n\\n/** @type {string} */\\nSpeechSynthesisUtterance.prototype.text;\\n\\n/** @type {string} */\\nSpeechSynthesisUtterance.prototype.lang;\\n\\n/** @type {number} */\\nSpeechSynthesisUtterance.prototype.pitch;\\n\\n/** @type {number} */\\nSpeechSynthesisUtterance.prototype.rate;\\n\\n/** @type {SpeechSynthesisVoice} */\\nSpeechSynthesisUtterance.prototype.voice;\\n\\n/** @type {number} */\\nSpeechSynthesisUtterance.prototype.volume;\\n\\n/**\\n * @param {Event} event\\n */\\nSpeechSynthesisUtterance.prototype.onstart = function(event) {};\\n\\n/**\\n * @param {Event} event\\n */\\nSpeechSynthesisUtterance.prototype.onend = function(event) {};\\n\\n/**\\n * @param {Event} event\\n */\\nSpeechSynthesisUtterance.prototype.onerror = function(event) {};\\n\\n/**\\n * @constructor\\n */\\nfunction SpeechSynthesisVoice() {}\\n\\n/** @type {string} */\\nSpeechSynthesisVoice.prototype.voiceURI;\\n\\n/** @type {string} */\\nSpeechSynthesisVoice.prototype.name;\\n\\n/** @type {string} */\\nSpeechSynthesisVoice.prototype.lang;\\n\\n/** @type {boolean} */\\nSpeechSynthesisVoice.prototype.localService;\\n\\n/** @type {boolean} */\\nSpeechSynthesisVoice.prototype.default;\\n\\n\\n/**\\n * @constructor\\n * @extends {Array}\\n */\\nfunction SpeechSynthesisVoiceList() {}\\n\\n\\n/**\\n * @interface\\n * @extends {EventTarget}\\n */\\nfunction SpeechSynthesis() {}\\n\\n/**\\n * @param {SpeechSynthesisUtterance} utterance\\n * @return {undefined}\\n */\\nSpeechSynthesis.prototype.speak = function(utterance) {};\\n\\n/** @type {function()} */\\nSpeechSynthesis.prototype.cancel;\\n\\n/** @type {function()} */\\nSpeechSynthesis.prototype.pause;\\n\\n/** @type {function()} */\\nSpeechSynthesis.prototype.resume;\\n\\n/**\\n * @return {SpeechSynthesisVoiceList}\\n */\\nSpeechSynthesis.prototype.getVoices = function() {};\\n\\n/**\\n * @param {Event} event\\n * @see https://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi-errata.html\\n */\\nSpeechSynthesis.prototype.onvoiceschanged = function(event) {};\\n","externs/w3c_touch_event.js":"/*\\n * Copyright 2015 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for W3C\'s Touch Events specification.\\n * @see http://www.w3.org/TR/touch-events/\\n * @externs\\n */\\n\\n/**\\n * @typedef {{\\n * identifier: number,\\n * target: !EventTarget,\\n * clientX: (number|undefined),\\n * clientY: (number|undefined),\\n * screenX: (number|undefined),\\n * screenY: (number|undefined),\\n * pageX: (number|undefined),\\n * pageY: (number|undefined),\\n * radiusX: (number|undefined),\\n * radiusY: (number|undefined),\\n * rotationAngle: (number|undefined),\\n * force: (number|undefined)\\n * }}\\n */\\nvar TouchInitDict;\\n\\n/**\\n * The Touch class represents a single touch on the surface. A touch is the\\n * presence or movement of a finger that is part of a unique multi-touch\\n * sequence.\\n * @see http://www.w3.org/TR/touch-events/#touch-interface\\n * @param {!TouchInitDict} touchInitDict\\n * @constructor\\n */\\nfunction Touch(touchInitDict) {}\\n\\n/**\\n * The x-coordinate of the touch\'s location relative to the window\'s viewport.\\n * @type {number}\\n */\\nTouch.prototype.clientX;\\n\\n/**\\n * The y-coordinate of the touch\'s location relative to the window\'s viewport.\\n * @type {number}\\n */\\nTouch.prototype.clientY;\\n\\n/**\\n * The unique identifier for this touch object.\\n * @type {number}\\n */\\nTouch.prototype.identifier;\\n\\n/**\\n * The x-coordinate of the touch\'s location in page coordinates.\\n * @type {number}\\n */\\nTouch.prototype.pageX;\\n\\n/**\\n * The y-coordinate of the touch\'s location in page coordinates.\\n * @type {number}\\n */\\nTouch.prototype.pageY;\\n\\n/**\\n * The x-coordinate of the touch\'s location in screen coordinates.\\n * @type {number}\\n */\\nTouch.prototype.screenX;\\n\\n/**\\n * The y-coordinate of the touch\'s location in screen coordinates.\\n * @type {number}\\n */\\nTouch.prototype.screenY;\\n\\n/**\\n * The target of this touch.\\n * @type {EventTarget}\\n */\\nTouch.prototype.target;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/touch-events-extensions/#widl-Touch-force\\n */\\nTouch.prototype.force;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/touch-events-extensions/#widl-Touch-radiusX\\n */\\nTouch.prototype.radiusX;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/touch-events-extensions/#widl-Touch-radiusY\\n */\\nTouch.prototype.radiusY;\\n\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/2011/WD-touch-events-20110505/#widl-Touch-rotationAngle\\n */\\nTouch.prototype.rotationAngle;\\n\\n\\n/**\\n * Creates a new Touch object.\\n * @see http://www.w3.org/TR/touch-events/#widl-Document-createTouch-Touch-WindowProxy-view-EventTarget-target-long-identifier-long-pageX-long-pageY-long-screenX-long-screenY\\n * @param {Window} view\\n * @param {EventTarget} target\\n * @param {number} identifier\\n * @param {number} pageX\\n * @param {number} pageY\\n * @param {number} screenX\\n * @param {number} screenY\\n * @return {Touch}\\n */\\nDocument.prototype.createTouch = function(view, target, identifier, pageX,\\n pageY, screenX, screenY) {};\\n\\n\\n/**\\n * The TouchList class is used to represent a collection of Touch objects.\\n * @see http://www.w3.org/TR/touch-events/#touchlist-interface\\n * @constructor\\n * @implements {IArrayLike}\\n */\\nfunction TouchList() {}\\n\\n/**\\n * The number of Touch objects in this TouchList object.\\n * @type {number}\\n */\\nTouchList.prototype.length;\\n\\n/**\\n * Returns the Touch object at the given index.\\n * @param {number} index\\n * @return {?Touch}\\n */\\nTouchList.prototype.item = function(index) {};\\n\\n/**\\n * @param {number} identifier\\n * @return {?Touch}\\n * @see http://www.w3.org/TR/touch-events-extensions/#widl-TouchList-identifiedTouch-Touch-long-identifier\\n */\\nTouchList.prototype.identifiedTouch = function(identifier) {};\\n\\n/**\\n * Creates a new TouchList object.\\n * @see http://www.w3.org/TR/touch-events/#widl-Document-createTouchList-TouchList-Touch-touches\\n * @param {Array} touches\\n * @return {TouchList}\\n */\\nDocument.prototype.createTouchList = function(touches) {};\\n\\n/**\\n * @record\\n * @extends {UIEventInit}\\n */\\nfunction TouchEventInit() {}\\n\\n/** @type {undefined|?EventTarget} */\\nTouchEventInit.prototype.relatedTarget;\\n\\n/** @type {undefined|!Array} */\\nTouchEventInit.prototype.touches;\\n\\n/** @type {undefined|!Array} */\\nTouchEventInit.prototype.targetTouches;\\n\\n/** @type {undefined|!Array} */\\nTouchEventInit.prototype.changedTouches;\\n\\n/**\\n * The TouchEvent class encapsulates information about a touch event.\\n *\\n *

      The system continually sends TouchEvent objects to an application as\\n * fingers touch and move across a surface. A touch event provides a snapshot of\\n * all touches during a multi-touch sequence, most importantly the touches that\\n * are new or have changed for a particular target. A multi-touch sequence\\n * begins when a finger first touches the surface. Other fingers may\\n * subsequently touch the surface, and all fingers may move across the surface.\\n * The sequence ends when the last of these fingers is lifted from the surface.\\n * An application receives touch event objects during each phase of any touch.\\n *

      \\n *\\n *

      The different types of TouchEvent objects that can occur are:\\n *

        \\n *
      • touchstart - Sent when a finger for a given event touches the surface.\\n *
      • touchmove - Sent when a given event moves on the surface.\\n *
      • touchend - Sent when a given event lifts from the surface.\\n *
      • touchcancel - Sent when the system cancels tracking for the touch.\\n *
      \\n * TouchEvent objects are combined together to form high-level GestureEvent\\n * objects that are also sent during a multi-touch sequence.

      \\n *\\n * @see http://www.w3.org/TR/touch-events/#touchevent-interface\\n * @param {string} type\\n * @param {!TouchEventInit=} opt_eventInitDict\\n * @extends {UIEvent}\\n * @constructor\\n */\\nfunction TouchEvent(type, opt_eventInitDict) {}\\n\\n/**\\n * A collection of Touch objects representing all touches associated with this\\n * target.\\n * @type {TouchList}\\n */\\nTouchEvent.prototype.touches;\\n\\n/**\\n * A collection of Touch objects representing all touches associated with this\\n * target.\\n * @type {TouchList}\\n */\\nTouchEvent.prototype.targetTouches;\\n\\n/**\\n * A collection of Touch objects representing all touches that changed in this event.\\n * @type {TouchList}\\n */\\nTouchEvent.prototype.changedTouches;\\n\\n/**\\n * @type {boolean}\\n */\\nTouchEvent.prototype.altKey;\\n\\n/**\\n * @type {boolean}\\n */\\nTouchEvent.prototype.metaKey;\\n\\n/**\\n * @type {boolean}\\n */\\nTouchEvent.prototype.ctrlKey;\\n\\n/**\\n * @type {boolean}\\n */\\nTouchEvent.prototype.shiftKey;\\n\\n\\n/**\\n * Specifies the JavaScript method to invoke when the system cancels tracking\\n * for the touch.\\n * @type {?function(!TouchEvent)}\\n */\\nElement.prototype.ontouchcancel;\\n\\n/**\\n * Specifies the JavaScript method to invoke when a given event lifts from the\\n * surface.\\n * @type {?function(!TouchEvent)}\\n */\\nElement.prototype.ontouchend;\\n\\n/**\\n * Specifies the JavaScript method to invoke when a finger for a given event\\n * moves on the surface.\\n * @type {?function(!TouchEvent)}\\n */\\nElement.prototype.ontouchmove;\\n\\n/**\\n * Specifies the JavaScript method to invoke when a finger for a given event\\n * touches the surface.\\n * @type {?function(!TouchEvent)}\\n */\\nElement.prototype.ontouchstart;\\n","externs/w3c_vibration.js":"/*\\n * Copyright 2017 The'; +a.a+=' Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for Vibration API based on \\"W3C Recommendation 18 October 2016\\"\\n * @see https://www.w3.org/TR/2016/REC-vibration-20161018/\\n *\\n * @externs\\n * @author vobruba.martin@gmail.com (Martin Vobruba)\\n */\\n\\n\\n/**\\n * @typedef {number|!Array}\\n * @see https://www.w3.org/TR/2016/REC-vibration-20161018/#idl-def-vibratepattern\\n */\\nvar VibratePattern;\\n\\n\\n/**\\n * @param {!VibratePattern} pattern\\n * @return {boolean}\\n * @see https://www.w3.org/TR/2016/REC-vibration-20161018/#idl-def-navigator-vibrate(vibratepattern)\\n */\\nNavigator.prototype.vibrate = function(pattern) {};\\n","externs/w3c_webcrypto.js":"/*\\n * Copyright 2015 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n\\n/**\\n * @fileoverview Definitions for W3C\'s Web Cryptography specification\\n * http://www.w3.org/TR/webCryptoAPI\\n * @externs\\n * @author chrismoon@google.com (Chris Moon)\\n * This file was created using the best practices as described in:\\n * chrome_extensions.js\\n */\\n\\n\\n/**\\n * @const\\n * @see http://www.w3.org/TR/webCryptoAPI\\n */\\nvar webCrypto = {};\\n\\n\\n/**\\n * @typedef {?{\\n * name: string\\n * }}\\n * @see http://www.w3.org/TR/WebCryptoAPI/#algorithm-dictionary\\n */\\nwebCrypto.Algorithm;\\n\\n\\n/**\\n * @typedef {string|!webCrypto.Algorithm}\\n * @see http://www.w3.org/TR/WebCryptoAPI/#dfn-AlgorithmIdentifier\\n */\\nwebCrypto.AlgorithmIdentifier;\\n\\n/**\\n * @typedef {webCrypto.AlgorithmIdentifier}\\n * @see http://www.w3.org/TR/WebCryptoAPI/#dfn-HashAlgorithmIdentifier\\n */\\nwebCrypto.HashAlgorithmIdentifier;\\n\\n\\n/**\\n * @typedef {Uint8Array}\\n * @see https://www.w3.org/TR/WebCryptoAPI/#dfn-BigInteger\\n */\\nwebCrypto.BigInteger;\\n\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/webCryptoAPI/#dfn-CryptoKey\\n */\\nwebCrypto.CryptoKey = function() {};\\n\\n\\n/**\\n * @type {string} An enumerated value representing the type of the key, a secret\\n * key (for symmetric algorithm), a public or a private key\\n * (for an asymmetric algorithm).\\n */\\nwebCrypto.CryptoKey.prototype.type;\\n\\n\\n/**\\n * @type {boolean} Determines whether or not the raw keying material may be\\n * exported by the application.\\n */\\nwebCrypto.CryptoKey.prototype.extractable;\\n\\n\\n/**\\n * @type {!Object} An opaque object representing a particular cipher the key\\n * has to be used with.\\n */\\nwebCrypto.CryptoKey.prototype.algorithm;\\n\\n\\n/**\\n * @type {!Object} Returns the cached ECMAScript object associated with the\\n * usages internal slot, which indicates which cryptographic operations are\\n * permissible to be used with this key.\\n */\\nwebCrypto.CryptoKey.prototype.usages;\\n\\n\\n/**\\n * @constructor\\n * @see https://www.w3.org/TR/WebCryptoAPI/#keypair\\n */\\nwebCrypto.CryptoKeyPair = function() {};\\n\\n\\n/**\\n * @type {!webCrypto.CryptoKey}\\n */\\nwebCrypto.CryptoKeyPair.prototype.publicKey;\\n\\n\\n/**\\n * @type {!webCrypto.CryptoKey}\\n */\\nwebCrypto.CryptoKeyPair.prototype.privateKey;\\n\\n\\n/**\\n * @typedef {?{\\n * name: string\\n * }}\\n * @see http://www.w3.org/TR/WebCryptoAPI/#key-algorithm-dictionary-members\\n */\\nwebCrypto.KeyAlgorithm;\\n\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/WebCryptoAPI/#dfn-JsonWebKey\\n * @see Section 3.1:\\n * https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41\\n */\\nwebCrypto.JsonWebKey = function() {};\\n\\n\\n/**\\n * @type {string} Identifies the cryptographic algorithm family used with\\n * the key, such as \\"RSA\\" or \\"EC\\".\\n */\\nwebCrypto.JsonWebKey.prototype.kty;\\n\\n\\n/**\\n * @type {string} Identifies the intended use of the public key.\\n */\\nwebCrypto.JsonWebKey.prototype.use;\\n\\n\\n/**\\n * @type {!Array} Identifies the operation(s) that the key is\\n * intended to be used for.\\n */\\nwebCrypto.JsonWebKey.prototype.key_ops;\\n\\n\\n/**\\n * @type {string} Identifies the algorithm intended for use with the key.\\n */\\nwebCrypto.JsonWebKey.prototype.alg;\\n\\n\\n/**\\n * @type {boolean} Boolean to be used with kty values.\\n */\\nwebCrypto.JsonWebKey.prototype.ext;\\n\\n\\n/**\\n * @type {string} Identifies the cryptographic curve used with the key.\\n */\\nwebCrypto.JsonWebKey.prototype.crv;\\n\\n\\n/**\\n * @type {string} Contains the x coordinate for the elliptic curve point.\\n */\\nwebCrypto.JsonWebKey.prototype.x;\\n\\n\\n/**\\n * @type {string} Contains the y coordinate for the elliptic curve point.\\n */\\nwebCrypto.JsonWebKey.prototype.y;\\n\\n\\n/**\\n * @type {string} Contains the Elliptic Curve private key value.\\n */\\nwebCrypto.JsonWebKey.prototype.d;\\n\\n\\n/**\\n * @type {string} Contains the modulus value for the RSA public key.\\n */\\nwebCrypto.JsonWebKey.prototype.n;\\n\\n\\n/**\\n * @type {string} Contains the exponent value for the RSA public key.\\n */\\nwebCrypto.JsonWebKey.prototype.e;\\n\\n\\n/**\\n * @type {string} Contains the first prime factor.\\n */\\nwebCrypto.JsonWebKey.prototype.p;\\n\\n\\n/**\\n * @type {string} Contains the second prime factor.\\n */\\nwebCrypto.JsonWebKey.prototype.q;\\n\\n\\n/**\\n * @type {string} Contains the Chinese Remainder Theorem (CRT) exponent of\\n * the first factor.\\n */\\nwebCrypto.JsonWebKey.prototype.dp;\\n\\n\\n/**\\n * @type {string} Contains the Chinese Remainder Theorem (CRT) exponent of\\n * the second factor.\\n */\\nwebCrypto.JsonWebKey.prototype.dq;\\n\\n\\n/**\\n * @type {string} Contains the Chinese Remainder Theorem (CRT) coefficient\\n * of the second factor.\\n */\\nwebCrypto.JsonWebKey.prototype.qi;\\n\\n\\n/**\\n * @type {!Array} Contains an array of\\n * information about any third and subsequent primes, should they exist.\\n */\\nwebCrypto.JsonWebKey.prototype.oth;\\n\\n\\n/**\\n * @type {string} Contains the value of the symmetric (or other\\n * single-valued) key.\\n */\\nwebCrypto.JsonWebKey.prototype.k;\\n\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/WebCryptoAPI/#dfn-RsaOtherPrimesInfo\\n * @see Section-6.3.2.7:\\n * https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40\\n */\\nwebCrypto.RsaOtherPrimesInfo = function() {};\\n\\n\\n/**\\n * @type {string} Parameter within an \\"oth\\" array member represents the value\\n * of a subsequent prime factor.\\n */\\nwebCrypto.RsaOtherPrimesInfo.prototype.r;\\n\\n\\n/**\\n * @type {string} Parameter within an \\"oth\\" array member represents the CRT\\n * exponent of the corresponding prime factor.\\n */\\nwebCrypto.RsaOtherPrimesInfo.prototype.d;\\n\\n\\n/**\\n * @type {string} Parameter within an \\"oth\\" array member represents the CRT\\n * coefficient of the corresponding prime factor.\\n */\\nwebCrypto.RsaOtherPrimesInfo.prototype.t;\\n\\n\\n/**\\n * @record\\n * @extends webCrypto.Algorithm\\n * @see https://www.w3.org/TR/WebCryptoAPI/#dfn-RsaKeyGenParams\\n */\\nwebCrypto.RsaKeyGenParams;\\n/**\\n * @type {number}\\n */\\nwebCrypto.RsaKeyGenParams.prototype.modulusLength;\\n/**\\n * @type {webCrypto.BigInteger}\\n */\\nwebCrypto.RsaKeyGenParams.prototype.publicExponent;\\n\\n\\n/**\\n * @record\\n * @extends webCrypto.RsaKeyGenParams\\n * @see https://www.w3.org/TR/WebCryptoAPI/#dfn-RsaHashedKeyGenParams\\n */\\nwebCrypto.RsaHashedKeyGenParams;\\n/**\\n * @type {webCrypto.HashAlgorithmIdentifier}\\n */\\nwebCrypto.RsaHashedKeyGenParams.prototype.hash;\\n\\n\\n/**\\n * @record\\n * @extends webCrypto.KeyAlgorithm\\n * @see https://www.w3.org/TR/WebCryptoAPI/#dfn-RsaKeyAlgorithm\\n */\\nwebCrypto.RsaKeyAlgorithm;\\n/**\\n * @type {number}\\n */\\nwebCrypto.RsaKeyAlgorithm.prototype.modulusLength;\\n/**\\n * @type {webCrypto.BigInteger}\\n */\\nwebCrypto.RsaKeyAlgorithm.prototype.publicExponent;\\n\\n\\n/**\\n * @record\\n * @extends webCrypto.RsaKeyAlgorithm\\n * @see https://www.w3.org/TR/WebCryptoAPI/#dfn-RsaHashedKeyAlgorithm\\n */\\nwebCrypto.RsaHashedKeyAlgorithm;\\n/**\\n * @type {webCrypto.KeyAlgorithm}\\n */\\nwebCrypto.RsaHashedKeyAlgorithm.prototype.hash;\\n\\n\\n/**\\n * @record\\n * @extends webCrypto.Algorithm\\n * @see https://www.w3.org/TR/WebCryptoAPI/#dfn-RsaHashedImportParams\\n */\\nwebCrypto.RsaHashedImportParams;\\n/**\\n * @type {webCrypto.HashAlgorithmIdentifier}\\n */\\nwebCrypto.RsaHashedImportParams.prototype.hash;\\n\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/WebCryptoAPI/#subtlecrypto-interface\\n */\\nwebCrypto.SubtleCrypto = function() {};\\n\\n\\n/**\\n * @param {!webCrypto.AlgorithmIdentifier} algorithm Supported\\n * values are: AES-CBC, AES-CTR, AES-GCM, and RSA-OAEP.\\n * @param {!webCrypto.CryptoKey} key Key to be used for signing.\\n * @param {!BufferSource} data Data to be encrypted (cleartext).\\n * @return {!Promise} Ciphertext generated by the encryption of\\n * the cleartext.\\n */\\nwebCrypto.SubtleCrypto.prototype.encrypt = function(algorithm, key,\\n data) {};\\n\\n\\n/**\\n * @param {!webCrypto.AlgorithmIdentifier} algorithm Supported\\n * values are: AES-CBC, AES-CTR, AES-GCM, and RSA-OAEP.\\n * @param {!webCrypto.CryptoKey} key Key to be used for signing.\\n * @param {!BufferSource} data Data to be decrypted (ciphertext).\\n * @return {!Promise} Cleartext generated by the decryption of the\\n * ciphertext.\\n */\\nwebCrypto.SubtleCrypto.prototype.decrypt = function(algorithm, key,\\n data) {};\\n\\n\\n/**\\n * @param {!webCrypto.AlgorithmIdentifier} algorithm Supported\\n * values are: HMAC, RSASSA-PKCS1-v1_5, and ECDSA.\\n * @param {!webCrypto.CryptoKey} key Private key to be used for signing.\\n * @param {!BufferSource} data Data to be signed.\\n * @return {!Promise} Returns the signature on success.\\n */\\nwebCrypto.SubtleCrypto.prototype.sign = function(algorithm, key,\\n data) {};\\n\\n\\n/**\\n * @param {!webCrypto.AlgorithmIdentifier} algorithm Supported\\n * values are: HMAC, RSASSA-PKCS1-v1_5, and ECDSA.\\n * @param {!webCrypto.CryptoKey} key Private key to be used for signing.\\n * @param {!BufferSource} signature Signature to verify.\\n * @param {!BufferSource} data Data whose signature needs to be verified.\\n * @return {!Promise} Returns if the signature operating has been\\n * successful.\\n */\\nwebCrypto.SubtleCrypto.prototype.verify = function(algorithm, key,\\n signature, data) {};\\n\\n\\n/**\\n * @param {!webCrypto.AlgorithmIdentifier} algorithm Supported\\n * values are: SHA-1, SHA-256, SHA-384, and SHA-512.\\n * @param {!BufferSource} data Data to be hashed using the hashing algorithm.\\n * @return {!Promise} returns the hash on success.\\n */\\nwebCrypto.SubtleCrypto.prototype.digest = function(algorithm, data) {};\\n\\n\\n/**\\n * @param {!webCrypto.AlgorithmIdentifier|webCrypto.RsaHashedKeyGenParams}\\n * algorithm Supported values are: SHA-1, SHA-256, SHA-384, and SHA-512.\\n * @param {boolean} extractable If the key can be extracted from the CryptoKey\\n * object at a later stage.\\n * @param {!Array} keyUsages Indication of new key options i.e.\\n * encrypt, decrypt, sign, verify, deriveKey, deriveBits, wrapKey,\\n * unwrapKey.\\n * @return {!Promise} returns the\\n * generated key.\\n */\\nwebCrypto.SubtleCrypto.prototype.generateKey = function(algorithm,\\n extractable, keyUsages) {};\\n\\n\\n/**\\n * @param {!webCrypto.AlgorithmIdentifier} algorithm The key derivation\\n * algorithm to use. Supported values are: ECDH, DH, PBKDF2, and HKDF-CTR.\\n * @param {!webCrypto.CryptoKey} baseKey Key to be used by the key\\n * derivation algorithm.\\n * @param {!webCrypto.AlgorithmIdentifier} derivedKeyAlgo Defines the key\\n * derivation algorithm to use.\\n * @param {boolean} extractable Indicates if the key can be extracted from the\\n * CryptoKey object at a later stage.\\n * @param {!Array} keyUsages Indicates what can be done with the\\n * derivated key.\\n * @return {!Promise} returns the\\n * generated key.\\n */\\nwebCrypto.SubtleCrypto.prototype.deriveKey = function(algorithm,\\n baseKey, derivedKeyAlgo, extractable, keyUsages) {};\\n\\n\\n/**\\n * @param {!webCrypto.AlgorithmIdentifier} algorithm The key derivation\\n * algorithm to use.\\n * @param {!webCrypto.CryptoKey} baseKey Key to be used by the key\\n * derivation algorithm.\\n * @param {number} length\\n * @return {!Promise} returns the generated bits.\\n */\\nwebCrypto.SubtleCrypto.prototype.deriveBits = function(algorithm,\\n baseKey, length) {};\\n\\n\\n/**\\n * @param {string} format Enumerated value describing the data\\n * format of the key to imported.\\n * @param {!BufferSource|!webCrypto.JsonWebKey} keyData The key\\n * in the given format.\\n * @param {!webCrypto.AlgorithmIdentifier|webCrypto.RsaHashedImportParams}\\n * algorithm Supported values are: AES-CTR, AES-CBC, AES-GCM, RSA-OAEP,\\n * AES-KW, HMAC, RSASSA-PKCS1-v1_5, ECDSA, ECDH, DH.\\n * @param {boolean} extractable If the key can be extracted from the CryptoKey\\n * object at a later stage.\\n * @param {!Array} keyUsages Indication of new key options i.e.\\n * encrypt, decrypt, sign, verify, deriveKey, deriveBits, wrapKey,\\n * unwrapKey.\\n * @return {!Promise} returns the generated key.\\n */\\nwebCrypto.SubtleCrypto.prototype.importKey = function(format, keyData,\\n algorithm, extractable, keyUsages) {};\\n\\n\\n/**\\n * @param {string} format Enumerated value describing the data\\n * format of the key to imported.\\n * @param {!webCrypto.CryptoKey} key CryptoKey to export.\\n * @return {!Promise} returns the key in the\\n * requested format.\\n */\\nwebCrypto.SubtleCrypto.prototype.exportKey = function(format, key) {};\\n\\n\\n/**\\n * @param {string} format Value describing the data format in which the key must\\n * be wrapped. It can be one of the following: raw, pkcs8, spki, jwk.\\n * @param {!webCrypto.CryptoKey} key CryptoKey to wrap.\\n * @param {!webCrypto.CryptoKey} wrappingKey CryptoKey used to perform\\n * the wrapping.\\n * @param {!webCrypto.AlgorithmIdentifier} wrapAlgorithm algorithm used\\n * to perform the wrapping. It is one of the following: AES-CBC, AES-CTR,\\n * AES-GCM, RSA-OAEP, and AES-KW.\\n * @return {!Promise} returns the wrapped key in the requested\\n * format.\\n */\\nwebCrypto.SubtleCrypto.prototype.wrapKey = function(format,\\n key, wrappingKey, wrapAlgorithm) {};\\n\\n\\n/**\\n * @param {string} format Value describing the data format in which the key must\\n * be wrapped. It can be one of the following: raw, pkcs8, spki, jwk.\\n * @param {!BufferSource} wrappedKey Contains the wrapped key in the given\\n * format.\\n * @param {!webCrypto.CryptoKey} unwrappingKey CryptoKey used to perform\\n * the unwrapping.\\n * @param {!webCrypto.AlgorithmIdentifier} unwrapAlgorithm Algorithm\\n * used to perform the unwrapping. It is one of the following: AES-CBC,\\n * AES-CTR, AES-GCM, RSA-OAEP, and AES-KW.\\n * @param {!webCrypto.AlgorithmIdentifier} unwrappedKeyAlgorithm\\n * Represents the algorithm of the wrapped key.\\n * @param {boolean} extractable Indicates if the key can be extracted from the\\n * CryptoKey object at a later stage.\\n * @param {!Array} keyUsages Indicates what can be done with the\\n * derivated key.\\n * @return {!Promise} returns the unwrapped key.\\n */\\nwebCrypto.SubtleCrypto.prototype.unwrapKey = function(format, wrappedKey,\\n'; +a.a+=' unwrappingKey, unwrapAlgorithm, unwrappedKeyAlgorithm, extractable,\\n keyUsages) {};\\n\\n\\n/**\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Crypto\\n * @interface\\n */\\nwebCrypto.Crypto = function() {};\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/window.crypto.getRandomValues\\n * @param {!ArrayBufferView} typedArray\\n * @return {!ArrayBufferView}\\n * @throws {Error}\\n */\\nwebCrypto.Crypto.prototype.getRandomValues = function(typedArray) {};\\n\\n/**\\n * @type {?webCrypto.SubtleCrypto}\\n * @see http://www.w3.org/TR/WebCryptoAPI/#Crypto-attribute-subtle\\n */\\nwebCrypto.Crypto.prototype.subtle;\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/window.crypto\\n * @type {!webCrypto.Crypto|undefined}\\n */\\nvar crypto;\\n","externs/w3c_xml.js":"/*\\n * Copyright 2008 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for W3C\'s XML related specifications.\\n * This file depends on w3c_dom2.js.\\n * The whole file has been fully type annotated.\\n *\\n * Provides the XML standards from W3C.\\n * Includes:\\n * XPath - Fully type annotated\\n * XMLHttpRequest - Fully type annotated\\n *\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html\\n * @see http://www.w3.org/TR/XMLHttpRequest/\\n * @see http://www.w3.org/TR/XMLHttpRequest2/\\n *\\n * @externs\\n * @author acleung@google.com (Alan Leung)\\n * @author stevey@google.com (Steve Yegge)\\n */\\n\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathException\\n */\\nfunction XPathException() {}\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#INVALID_EXPRESSION_ERR\\n */\\nXPathException.INVALID_EXPRESSION_ERR = 52;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#TYPE_ERR\\n */\\nXPathException.TYPE_ERR = 52;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#\\n */\\nXPathException.prototype.code;\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathEvaluator\\n */\\nfunction XPathEvaluator() {}\\n\\n/**\\n * @param {string} expr\\n * @param {?XPathNSResolver=} opt_resolver\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathEvaluator-createExpression\\n * @throws XPathException\\n * @throws DOMException\\n * @return {undefined}\\n */\\nXPathEvaluator.prototype.createExpression = function(expr, opt_resolver) {};\\n\\n/**\\n * @param {Node} nodeResolver\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathEvaluator-createNSResolver\\n * @return {undefined}\\n */\\nXPathEvaluator.prototype.createNSResolver = function(nodeResolver) {};\\n\\n/**\\n * @param {string} expr\\n * @param {Node} contextNode\\n * @param {?XPathNSResolver=} opt_resolver\\n * @param {?number=} opt_type\\n * @param {*=} opt_result\\n * @return {XPathResult}\\n * @throws XPathException\\n * @throws DOMException\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathEvaluator-evaluate\\n */\\nXPathEvaluator.prototype.evaluate = function(expr, contextNode, opt_resolver,\\n opt_type, opt_result) {};\\n\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathExpression\\n */\\nfunction XPathExpression() {}\\n\\n/**\\n * @param {Node} contextNode\\n * @param {number=} opt_type\\n * @param {*=} opt_result\\n * @return {*}\\n * @throws XPathException\\n * @throws DOMException\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathExpression-evaluate\\n */\\nXPathExpression.prototype.evaluate = function(contextNode, opt_type,\\n opt_result) {};\\n\\n\\n/**\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathNSResolver\\n */\\nfunction XPathNSResolver() {}\\n\\n/**\\n * @param {string} prefix\\n * @return {?string}\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathNSResolver-lookupNamespaceURI\\n */\\nXPathNSResolver.prototype.lookupNamespaceURI = function(prefix) {};\\n\\n/**\\n * From http://www.w3.org/TR/xpath\\n *\\n * XPath is a language for addressing parts of an XML document, designed to be\\n * used by both XSLT and XPointer.\\n *\\n * @constructor\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult\\n */\\nfunction XPathResult() {}\\n\\n/**\\n * @type {boolean} {@see XPathException.TYPE_ERR}\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-booleanValue\\n */\\nXPathResult.prototype.booleanValue;\\n\\n/**\\n * @type {boolean} {@see XPathException.TYPE_ERR}\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-invalid-iterator-state\\n */\\nXPathResult.prototype.invalidInteratorState;\\n\\n/**\\n * @type {number}\\n * @throws XPathException {@see XPathException.TYPE_ERR}\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-numberValue\\n */\\nXPathResult.prototype.numberValue;\\n\\n/**\\n * @type {number}\\n * @throws XPathException {@see XPathException.TYPE_ERR}\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-resultType\\n */\\nXPathResult.prototype.resultType;\\n\\n/**\\n * @type {Node}\\n * @throws XPathException {@see XPathException.TYPE_ERR}\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-singleNodeValue\\n */\\nXPathResult.prototype.singleNodeValue;\\n\\n/**\\n * @type {number}\\n * @throws XPathException {@see XPathException.TYPE_ERR}\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-snapshot-length\\n */\\nXPathResult.prototype.snapshotLength;\\n\\n/**\\n * @type {string}\\n * @throws XPathException {@see XPathException.TYPE_ERR}\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-stringValue\\n */\\nXPathResult.prototype.stringValue;\\n\\n/**\\n * @return {Node}\\n * @throws XPathException {@see XPathException.TYPE_ERR}\\n * @throws DOMException {@see DOMException.INVALID_STATE_ERR}\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-iterateNext\\n */\\nXPathResult.prototype.iterateNext = function() {};\\n\\n/**\\n * @param {number} index\\n * @return {Node}\\n * @throws XPathException\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-snapshotItem\\n */\\nXPathResult.prototype.snapshotItem = function(index) {};\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-ANY-TYPE\\n */\\nXPathResult.ANY_TYPE = 0;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-NUMBER-TYPE\\n */\\nXPathResult.NUMBER_TYPE = 1;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-STRING-TYPE\\n */\\nXPathResult.STRING_TYPE = 2;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-BOOLEAN-TYPE\\n */\\nXPathResult.BOOLEAN_TYPE = 3;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-UNORDERED-NODE-ITERATOR-TYPE\\n */\\nXPathResult.UNORDERED_NODE_ITERATOR_TYPE = 4;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-ORDERED-NODE-ITERATOR-TYPE\\n */\\nXPathResult.ORDERED_NODE_ITERATOR_TYPE = 5;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-UNORDERED-NODE-SNAPSHOT-TYPE\\n */\\nXPathResult.UNORDERED_NODE_SNAPSHOT_TYPE = 6;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-ORDERED-NODE-SNAPSHOT-TYPE\\n */\\nXPathResult.ORDERED_NODE_SNAPSHOT_TYPE = 7;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-ANY-UNORDERED-NODE-TYPE\\n */\\nXPathResult.ANY_UNORDERED_NODE_TYPE = 8;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-FIRST-ORDERED-NODE-TYPE\\n */\\nXPathResult.FIRST_ORDERED_NODE_TYPE = 9;\\n\\n/**\\n * @constructor\\n * @extends {Node}\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathNamespace\\n */\\nfunction XPathNamespace() {}\\n\\n/**\\n * @type {Element}\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathNamespace-ownerElement\\n */\\nXPathNamespace.prototype.ownerElement;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPATH_NAMESPACE_NODE\\n */\\nXPathNamespace.XPATH_NAMESPACE_NODE = 13;\\n\\n/**\\n * From http://www.w3.org/TR/XMLHttpRequest/\\n *\\n * (Draft)\\n *\\n * The XMLHttpRequest Object specification defines an API that provides\\n * scripted client functionality for transferring data between a client and a\\n * server.\\n *\\n * @constructor\\n * @implements {EventTarget}\\n * @see http://www.w3.org/TR/XMLHttpRequest/#xmlhttprequest-object\\n */\\nfunction XMLHttpRequest() {}\\n\\n/** @override */\\nXMLHttpRequest.prototype.addEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nXMLHttpRequest.prototype.removeEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nXMLHttpRequest.prototype.dispatchEvent = function(evt) {};\\n\\n/**\\n * @param {string} method\\n * @param {string} url\\n * @param {?boolean=} opt_async\\n * @param {?string=} opt_user\\n * @param {?string=} opt_password\\n * @return {undefined}\\n * @see http://www.w3.org/TR/XMLHttpRequest/#the-open()-method\\n */\\nXMLHttpRequest.prototype.open = function(method, url, opt_async, opt_user,\\n opt_password) {};\\n\\n/**\\n * @param {string} header\\n * @param {string} value\\n * @return {undefined}\\n * @see http://www.w3.org/TR/XMLHttpRequest/#the-setrequestheader()-method\\n */\\nXMLHttpRequest.prototype.setRequestHeader = function(header, value) {};\\n\\n/**\\n * @param {ArrayBuffer|ArrayBufferView|Blob|Document|FormData|string=} opt_data\\n * @return {undefined}\\n * @see http://www.w3.org/TR/XMLHttpRequest/#the-send()-method\\n */\\nXMLHttpRequest.prototype.send = function(opt_data) {};\\n\\n/**\\n * @return {undefined}\\n * @see http://www.w3.org/TR/XMLHttpRequest/#the-abort()-method\\n */\\nXMLHttpRequest.prototype.abort = function() {};\\n\\n/**\\n * @return {string}\\n * @see http://www.w3.org/TR/XMLHttpRequest/#the-getallresponseheaders()-method\\n */\\nXMLHttpRequest.prototype.getAllResponseHeaders = function() {};\\n\\n/**\\n * @param {string} header\\n * @return {string}\\n * @see http://www.w3.org/TR/XMLHttpRequest/#the-getresponseheader()-method\\n */\\nXMLHttpRequest.prototype.getResponseHeader = function(header) {};\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/XMLHttpRequest/#the-responsetext-attribute\\n */\\nXMLHttpRequest.prototype.responseText;\\n\\n/**\\n * This is not supported in any IE browser (as of August 2016).\\n * @type {string}\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseURL\\n */\\nXMLHttpRequest.prototype.responseURL;\\n\\n/**\\n * @type {Document}\\n * @see http://www.w3.org/TR/XMLHttpRequest/#the-responsexml-attribute\\n */\\nXMLHttpRequest.prototype.responseXML;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/XMLHttpRequest/#the-readystate-attribute\\n */\\nXMLHttpRequest.prototype.readyState;\\n\\n/**\\n * @type {number}\\n * @see http://www.w3.org/TR/XMLHttpRequest/#the-status-attribute\\n */\\nXMLHttpRequest.prototype.status;\\n\\n/**\\n * @type {string}\\n * @see http://www.w3.org/TR/XMLHttpRequest/#the-statustext-attribute\\n */\\nXMLHttpRequest.prototype.statusText;\\n\\n/**\\n * @type {?function(!Event)}\\n * @see http://www.w3.org/TR/XMLHttpRequest/#handler-xhr-onreadystatechange\\n */\\nXMLHttpRequest.prototype.onreadystatechange;\\n\\n/**\\n * @type {?function(!Event)}\\n * @see http://www.w3.org/TR/XMLHttpRequest/#handler-xhr-onerror\\n */\\nXMLHttpRequest.prototype.onerror;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/XMLHttpRequest/#states\\n */\\nXMLHttpRequest.UNSENT;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/XMLHttpRequest/#states\\n */\\nXMLHttpRequest.OPENED;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/XMLHttpRequest/#states\\n */\\nXMLHttpRequest.HEADERS_RECEIVED;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/XMLHttpRequest/#states\\n */\\nXMLHttpRequest.LOADING;\\n\\n/**\\n * @type {number}\\n * @see https://www.w3.org/TR/XMLHttpRequest/#states\\n */\\nXMLHttpRequest.DONE;\\n\\n/**\\n * The FormData object represents an ordered collection of entries. Each entry\\n * has a name and value.\\n *\\n * @param {?Element=} opt_form An optional form to use for constructing the form\\n * data set.\\n * @constructor\\n * @see http://www.w3.org/TR/XMLHttpRequest2/#the-formdata-interface\\n */\\nfunction FormData(opt_form) {}\\n\\n/**\\n * @param {string} name\\n * @param {Blob|string} value\\n * @param {string=} opt_filename\\n * @return {undefined}\\n */\\nFormData.prototype.append = function(name, value, opt_filename) {};\\n","externs/window.js":"/*\\n * Copyright 2008 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview JavaScript Built-Ins for windows properties.\\n *\\n * @externs\\n * @author acleung@google.com (Alan Leung)\\n * @author stevey@google.com (Steve Yegge)\\n */\\n\\n// Window properties\\n// Only common properties are here. Others such as open()\\n// should be used with an explicit Window object.\\n\\n/**\\n * @type {!Window}\\n * @see https://developer.mozilla.org/en/DOM/window.top\\n * @const\\n */\\nvar top;\\n\\n/**\\n * @type {!Navigator}\\n * @see https://developer.mozilla.org/en/DOM/window.navigator\\n * @const\\n */\\nvar navigator;\\n\\n/**\\n * @type {!HTMLDocument}\\n * @see https://developer.mozilla.org/en/DOM/window.document\\n * @const\\n */\\nvar document;\\n\\n/**\\n * @type {!Location}\\n * @see https://developer.mozilla.org/en/DOM/window.location\\n * @const\\n * @suppress {duplicate}\\n * @implicitCast\\n */\\nvar location;\\n\\n/**\\n * @type {!Screen}\\n * @see https://developer.mozilla.org/En/DOM/window.screen\\n * @const\\n */\\nvar screen;\\n\\n/**\\n * @type {!Window}\\n * @see https://developer.mozilla.org/En/DOM/Window.self\\n * @const\\n */\\nvar self;\\n\\n/**\\n * @type {boolean}\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/isSecureContext\\n * @const\\n */\\nvar isSecureContext;\\n\\n/**\\n * @type {!VisualViewport}\\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/visualViewport\\n * @const\\n */\\nvar visualViewport;\\n\\n// Magic functions for Firefox\'s LiveConnect.\\n// We\'ll probably never use these in practice. But redefining them\\n// will fire up the JVM, so we want to reserve the symbol names.\\n\\n/**\\n * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/JavaArray\\n */\\nvar JavaArray;\\n\\n/**\\n * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/JavaClass\\n */\\nvar JavaClass;\\n\\n// We just ripped this from the FF source; it doesn\'t appear to be\\n// publicly documented.\\nvar JavaMember;\\n\\n/**\\n * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/JavaObject\\n */\\nvar JavaObject;\\n\\n/**\\n * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/JavaPackage\\n */\\nvar JavaPackage;\\n\\n/**\\n * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Packages\\n */\\nvar Packages;\\n\\n/**\\n * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/java\\n */\\nvar java;\\n\\n/**\\n * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/netscape\\n */\\nvar netscape;\\n\\n/**'; +a.a+='\\n * @param {*} message\\n * @see https://developer.mozilla.org/en/DOM/window.alert\\n * @return {undefined}\\n */\\nfunction alert(message) {}\\n\\n/**\\n * @param {number|undefined|null} immediateID\\n * @see https://developer.mozilla.org/en-US/docs/DOM/window.clearImmediate\\n * @see http://msdn.microsoft.com/en-us/library/ie/hh924825(v=vs.85).aspx\\n * @return {undefined}\\n */\\nfunction clearImmediate(immediateID) {}\\n\\n/**\\n * @param {number|undefined?} intervalID\\n * @see https://developer.mozilla.org/en/DOM/window.clearInterval\\n * @suppress {duplicate}\\n * @return {undefined}\\n */\\nfunction clearInterval(intervalID) {}\\n\\n/**\\n * @param {number|undefined?} timeoutID\\n * @see https://developer.mozilla.org/en/DOM/window.clearTimeout\\n * @suppress {duplicate}\\n * @return {undefined}\\n */\\nfunction clearTimeout(timeoutID) {}\\n\\n/**\\n * @param {*} message\\n * @return {boolean}\\n * @see https://developer.mozilla.org/en/DOM/window.confirm\\n */\\nfunction confirm(message) {}\\n\\n/**\\n * @see https://developer.mozilla.org/en/DOM/window.dump\\n * @param {*} x\\n * @return {undefined}\\n */\\nfunction dump(x) {}\\n\\n/**\\n * @param {string} message\\n * @param {string=} opt_value\\n * @return {?string}\\n * @see https://developer.mozilla.org/en/DOM/window.prompt\\n */\\nfunction prompt(message, opt_value) {}\\n\\n/**\\n * @param {function()} callback\\n * @param {...?} callbackParams\\n * @return {number}\\n * @see https://developer.mozilla.org/en-US/docs/DOM/window.setImmediate\\n * @see http://msdn.microsoft.com/en-us/library/ie/hh773176(v=vs.85).aspx\\n */\\nfunction setImmediate(callback, callbackParams) {}\\n\\n/**\\n * @param {Function|!TrustedScript|string} callback\\n * @param {number=} opt_delay\\n * @param {...?} callbackParams\\n * @return {number}\\n * @see https://developer.mozilla.org/en/DOM/window.setInterval\\n * @see https://html.spec.whatwg.org/multipage/webappapis.html#timers\\n */\\nfunction setInterval(callback, opt_delay, callbackParams) {}\\n\\n/**\\n * @param {Function|!TrustedScript|string} callback\\n * @param {number=} opt_delay\\n * @param {...*} callbackParams\\n * @return {number}\\n * @see https://developer.mozilla.org/en/DOM/window.setTimeout\\n * @see https://html.spec.whatwg.org/multipage/webappapis.html#timers\\n */\\nfunction setTimeout(callback, opt_delay, callbackParams) {}\\n\\n/**\\n * Returns whether the object has a property with the specified name.\\n *\\n * @param {*} propertyName Implicitly cast to a string.\\n * @return {boolean}\\n * @nosideeffects\\n * @see http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty\\n */\\nfunction hasOwnProperty (propertyName) {}\\n","externs/webkit_notifications.js":"/*\\n * Copyright 2010 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n/**\\n * @fileoverview Definitions for W3C\'s Notifications specification.\\n * @externs\\n * @author atwilson@google.com (Drew Wilson)\\n */\\n\\n/**\\n * @typedef {{\\n * dir: (string|undefined),\\n * lang: (string|undefined),\\n * body: (string|undefined),\\n * tag: (string|undefined),\\n * image: (string|undefined),\\n * icon: (string|undefined),\\n * badge: (string|undefined),\\n * vibrate: (!Array|undefined),\\n * timestamp: (number|undefined),\\n * renotify: (boolean|undefined),\\n * silent: (boolean|undefined),\\n * requireInteraction: (boolean|undefined),\\n * data: (*|undefined),\\n * actions: (!Array|undefined),\\n * }}\\n * TODO(rsk): change the type of data to a serializable object\\n * (https://html.spec.whatwg.org/multipage/structured-data.html).\\n *\\n * @see http://notifications.spec.whatwg.org/#notification\\n */\\nvar NotificationOptions;\\n\\n/**\\n * @typedef {{action: string, title: string, icon: (string|undefined)}}\\n * @see https://notifications.spec.whatwg.org/#dictdef-notificationoptions\\n */\\nvar NotificationAction;\\n\\n/**\\n * @typedef {{tag: (string|undefined)}}\\n * @see https://notifications.spec.whatwg.org/#dictdef-getnotificationoptions\\n */\\nvar GetNotificationOptions;\\n\\n/** @interface */\\nvar NotificationOptionsInterface_ = function() {}\\n/** @type {string} */ NotificationOptionsInterface_.prototype.dir;\\n/** @type {string} */ NotificationOptionsInterface_.prototype.lang;\\n/** @type {string} */ NotificationOptionsInterface_.prototype.body;\\n/** @type {string} */ NotificationOptionsInterface_.prototype.tag;\\n/** @type {string} */ NotificationOptionsInterface_.prototype.icon;\\n/** @type {boolean} */\\n NotificationOptionsInterface_.prototype.requireInteraction;\\n\\n/**\\n * @param {string} title\\n * @param {NotificationOptions=} opt_options\\n * @constructor\\n * @implements {EventTarget}\\n * @see http://notifications.spec.whatwg.org/#notification\\n */\\nfunction Notification(title, opt_options) {}\\n\\n/**\\n * @type {string}\\n */\\nNotification.permission;\\n\\n/**\\n * @param {NotificationPermissionCallback=} opt_callback\\n * @return {!Promise}\\n */\\nNotification.requestPermission = function(opt_callback) {};\\n\\n/** @override */\\nNotification.prototype.addEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nNotification.prototype.removeEventListener = function(\\n type, listener, opt_options) {};\\n\\n/** @override */\\nNotification.prototype.dispatchEvent = function(evt) {};\\n\\n/**\\n * @type {string}\\n */\\nNotification.prototype.title;\\n\\n/**\\n * @type {string}\\n */\\nNotification.prototype.body;\\n\\n/**\\n * @type {string}\\n */\\nNotification.prototype.icon;\\n\\n/**\\n * The string used by clients to identify the notification.\\n * @type {string}\\n */\\nNotification.prototype.tag;\\n\\n/**\\n * The ID used by clients to uniquely identify notifications to eliminate\\n * duplicate notifications.\\n * @type {string}\\n * @deprecated Use NotificationOptions.tag instead.\\n */\\nNotification.prototype.replaceId;\\n\\n/**\\n * @type {*}\\n */\\nNotification.prototype.data;\\n\\n/**\\n * The string used by clients to specify the directionality (rtl/ltr) of the\\n * notification.\\n * @type {string}\\n * @deprecated Use NotificationOptions.titleDir and bodyDir instead.\\n */\\nNotification.prototype.dir;\\n\\n/**\\n * Displays the notification.\\n * @return {undefined}\\n */\\nNotification.prototype.show = function() {};\\n\\n/**\\n * Prevents the notification from being displayed, or closes it if it is already\\n * displayed.\\n * @return {undefined}\\n */\\nNotification.prototype.cancel = function() {};\\n\\n/**\\n * Prevents the notification from being displayed, or closes it if it is already\\n * displayed.\\n * @return {undefined}\\n */\\nNotification.prototype.close = function() {};\\n\\n/**\\n * An event handler called when notification is closed.\\n * @type {?function(Event)}\\n */\\nNotification.prototype.onclose;\\n\\n/**\\n * An event handler called if the notification could not be displayed due to\\n * an error (i.e. resource could not be loaded).\\n * @type {?function(Event)}\\n */\\nNotification.prototype.onerror;\\n\\n/**\\n * An event handler called when the notification has become visible.\\n * @type {?function(Event)}\\n * @deprecated Use onshow instead.\\n */\\nNotification.prototype.ondisplay;\\n\\n/**\\n * An event handler called when the notification has become visible.\\n * @type {?function(Event)}\\n */\\nNotification.prototype.onshow;\\n\\n/**\\n * An event handler called when the notification has been clicked on.\\n * @type {?function(Event)}\\n */\\nNotification.prototype.onclick;\\n\\n\\n\\n/**\\n * @typedef {function(string)}\\n * @see http://notifications.spec.whatwg.org/#notificationpermissioncallback\\n */\\nvar NotificationPermissionCallback;\\n\\n/**\\n * @constructor\\n * @see http://dev.w3.org/2006/webapi/WebNotifications/publish/#dialog-if\\n * @deprecated Use Notification instead.\\n */\\nfunction NotificationCenter() {}\\n\\n/**\\n * Creates a text+icon notification and displays it to the user.\\n * @param {string} iconUrl\\n * @param {string} title\\n * @param {string} body\\n * @return {Notification}\\n */\\nNotificationCenter.prototype.createNotification =\\n function(iconUrl, title, body) {};\\n\\n/**\\n * Creates an HTML notification and displays it to the user.\\n * @param {string} url\\n * @return {Notification}\\n */\\nNotificationCenter.prototype.createHTMLNotification = function(url) {};\\n\\n/**\\n * Checks if the user has permission to display notifications.\\n * @return {number}\\n */\\nNotificationCenter.prototype.checkPermission = function() {};\\n\\n/**\\n * Requests permission from the user to display notifications.\\n * @param {Function=} opt_callback\\n * @return {void}\\n */\\nNotificationCenter.prototype.requestPermission = function(opt_callback) {};\\n\\n/**\\n * WebKit browsers expose the NotificationCenter API through\\n * window.webkitNotifications.\\n * @type {NotificationCenter}\\n */\\nWindow.prototype.webkitNotifications;\\n\\n\\n/**\\n * @see https://notifications.spec.whatwg.org/#notificationevent\\n * @constructor\\n * @param {string} type\\n * @param {!ExtendableEventInit=} opt_eventInitDict\\n * @extends {ExtendableEvent}\\n */\\nfunction NotificationEvent(type, opt_eventInitDict) {}\\n\\n/** @type {!Notification} */\\nNotificationEvent.prototype.notification;\\n\\n/** @type {string} */\\nNotificationEvent.prototype.action;\\n","externs/webkit_usercontent.js":"/*\\n * Copyright 2016 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n/**\\n * @fileoverview Definitions for WKWebView\'s User Content interface.\\n * https://developer.apple.com/library/prerelease/ios/documentation/WebKit/Reference/WKUserContentController_Ref/\\n * https://trac.webkit.org/browser/trunk/Source/WebCore/page/WebKitNamespace.h\\n *\\n * @externs\\n */\\n\\n/** @constructor */\\nfunction WebKitNamespace() {}\\n\\n\\n/**\\n * @type {!UserMessageHandlersNamespace}\\n */\\nWebKitNamespace.prototype.messageHandlers;\\n\\n\\n/**\\n * @constructor\\n * @implements {IObject}\\n */\\nfunction UserMessageHandlersNamespace() {}\\n\\n\\n/** @constructor */\\nfunction UserMessageHandler() {}\\n\\n\\n/**\\n * @param {*} message\\n * @return {undefined}\\n */\\nUserMessageHandler.prototype.postMessage = function(message) {};\\n\\n\\n/**\\n * @type {!WebKitNamespace}\\n * @const\\n */\\nvar webkit;\\n","externs/webgl.js":"/*\\n * Copyright 2010 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for WebGL functions as described at\\n * http://www.khronos.org/registry/webgl/specs/latest/\\n *\\n * This file is current up to the WebGL 1.0.1 spec, including extensions.\\n *\\n * This relies on html5.js being included for Canvas and Typed Array support.\\n *\\n * This includes some extensions defined at\\n * http://www.khronos.org/registry/webgl/extensions/\\n *\\n * @externs\\n */\\n\\n\\n/**\\n * @typedef {ImageBitmap|ImageData|HTMLImageElement|HTMLCanvasElement|\\n * HTMLVideoElement|OffscreenCanvas}\\n */\\nvar TexImageSource;\\n\\n/**\\n * @constructor\\n */\\nfunction WebGLRenderingContext() {}\\n\\n\\n/** @type {number} */\\nWebGLRenderingContext.DEPTH_BUFFER_BIT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.STENCIL_BUFFER_BIT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.COLOR_BUFFER_BIT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.POINTS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.LINES;\\n\\n/** @type {number} */\\nWebGLRenderingContext.LINE_LOOP;\\n\\n/** @type {number} */\\nWebGLRenderingContext.LINE_STRIP;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TRIANGLES;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TRIANGLE_STRIP;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TRIANGLE_FAN;\\n\\n/** @type {number} */\\nWebGLRenderingContext.ZERO;\\n\\n/** @type {number} */\\nWebGLRenderingContext.ONE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.SRC_COLOR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.ONE_MINUS_SRC_COLOR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.SRC_ALPHA;\\n\\n/** @type {number} */\\nWebGLRenderingContext.ONE_MINUS_SRC_ALPHA;\\n\\n/** @type {number} */\\nWebGLRenderingContext.DST_ALPHA;\\n\\n/** @type {number} */\\nWebGLRenderingContext.ONE_MINUS_DST_ALPHA;\\n\\n/** @type {number} */\\nWebGLRenderingContext.DST_COLOR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.ONE_MINUS_DST_COLOR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.SRC_ALPHA_SATURATE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FUNC_ADD;\\n\\n/** @type {number} */\\nWebGLRenderingContext.BLEND_EQUATION;\\n\\n/** @type {number} */\\nWebGLRenderingContext.BLEND_EQUATION_RGB;\\n\\n/** @type {number} */\\nWebGLRenderingContext.BLEND_EQUATION_ALPHA;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FUNC_SUBTRACT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FUNC_REVERSE_SUBTRACT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.BLEND_DST_RGB;\\n\\n/** @type {number} */\\nWebGLRenderingContext.BLEND_SRC_RGB;\\n\\n/** @type {number} */\\nWebGLRenderingContext.BLEND_DST_ALPHA;\\n\\n/** @type {number} */\\nWebGLRenderingContext.BLEND_SRC_ALPHA;\\n\\n/** @type {number} */\\nWebGLRenderingContext.CONSTANT_COLOR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.ONE_MINUS_CONSTANT_COLOR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.CONSTANT_ALPHA;\\n\\n/** @type {number} */\\nWebGLRenderingContext.ONE_MINUS_CONSTANT_ALPHA;\\n\\n/** @type {number} */\\nWebGLRenderingContext.BLEND_COLOR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.ARRAY_BUFFER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.ELEMENT_ARRAY_BUFFER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.ARRAY_BUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGLRenderingContext.ELEMENT_ARRAY_BUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGLRenderingContext.STREAM_DRAW;\\n\\n/** @type {number} */\\nWebGLRenderingContext.STATIC_DRAW;\\n\\n/** @type {number} */\\nWebGLRenderingContext.DYNAMIC_DRAW;\\n\\n/** @type {number} */\\nWebGLRenderingContext.BUFFER_SIZE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.BUFFER_USAGE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.CURRENT_VERTEX_ATTRIB;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FRONT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.BACK;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FRONT_AND_BACK;\\n\\n/** @type {number} */\\nWebGLRenderingContext.CULL_FACE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.BLEND;\\n\\n/** @type {number} */\\nWebGLRenderingContext.DITHER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.STENCIL_TEST;\\n\\n/** @type {number} */\\nWebGLRenderingContext.DEPTH_TEST;\\n\\n/** @type {number} */\\nWebGLRenderingContext.SCISSOR_TEST;\\n\\n/** @type {number} */\\nWebGLRenderingContext.POLYGON_OFFSET_FILL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.SAMPLE_ALPHA_TO_COVERAGE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.SAMPLE_COVERAGE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.NO_ERROR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.INVALID_ENUM;\\n\\n/** @type {number} */\\nWebGLRenderingContext.INVALID_VALUE;\\n\\n/** @type {number} */\\nWebGLRenderingContext'; +a.a+=".INVALID_OPERATION;\\n\\n/** @type {number} */\\nWebGLRenderingContext.OUT_OF_MEMORY;\\n\\n/** @type {number} */\\nWebGLRenderingContext.CW;\\n\\n/** @type {number} */\\nWebGLRenderingContext.CCW;\\n\\n/** @type {number} */\\nWebGLRenderingContext.LINE_WIDTH;\\n\\n/** @type {number} */\\nWebGLRenderingContext.ALIASED_POINT_SIZE_RANGE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.ALIASED_LINE_WIDTH_RANGE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.CULL_FACE_MODE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FRONT_FACE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.DEPTH_RANGE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.DEPTH_WRITEMASK;\\n\\n/** @type {number} */\\nWebGLRenderingContext.DEPTH_CLEAR_VALUE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.DEPTH_FUNC;\\n\\n/** @type {number} */\\nWebGLRenderingContext.STENCIL_CLEAR_VALUE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.STENCIL_FUNC;\\n\\n/** @type {number} */\\nWebGLRenderingContext.STENCIL_FAIL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.STENCIL_PASS_DEPTH_FAIL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.STENCIL_PASS_DEPTH_PASS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.STENCIL_REF;\\n\\n/** @type {number} */\\nWebGLRenderingContext.STENCIL_VALUE_MASK;\\n\\n/** @type {number} */\\nWebGLRenderingContext.STENCIL_WRITEMASK;\\n\\n/** @type {number} */\\nWebGLRenderingContext.STENCIL_BACK_FUNC;\\n\\n/** @type {number} */\\nWebGLRenderingContext.STENCIL_BACK_FAIL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.STENCIL_BACK_PASS_DEPTH_FAIL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.STENCIL_BACK_PASS_DEPTH_PASS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.STENCIL_BACK_REF;\\n\\n/** @type {number} */\\nWebGLRenderingContext.STENCIL_BACK_VALUE_MASK;\\n\\n/** @type {number} */\\nWebGLRenderingContext.STENCIL_BACK_WRITEMASK;\\n\\n/** @type {number} */\\nWebGLRenderingContext.VIEWPORT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.SCISSOR_BOX;\\n\\n/** @type {number} */\\nWebGLRenderingContext.COLOR_CLEAR_VALUE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.COLOR_WRITEMASK;\\n\\n/** @type {number} */\\nWebGLRenderingContext.UNPACK_ALIGNMENT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.PACK_ALIGNMENT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.MAX_TEXTURE_SIZE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.MAX_VIEWPORT_DIMS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.SUBPIXEL_BITS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.RED_BITS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.GREEN_BITS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.BLUE_BITS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.ALPHA_BITS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.DEPTH_BITS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.STENCIL_BITS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.POLYGON_OFFSET_UNITS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.POLYGON_OFFSET_FACTOR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE_BINDING_2D;\\n\\n/** @type {number} */\\nWebGLRenderingContext.SAMPLE_BUFFERS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.SAMPLES;\\n\\n/** @type {number} */\\nWebGLRenderingContext.SAMPLE_COVERAGE_VALUE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.SAMPLE_COVERAGE_INVERT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.COMPRESSED_TEXTURE_FORMATS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.DONT_CARE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FASTEST;\\n\\n/** @type {number} */\\nWebGLRenderingContext.NICEST;\\n\\n/** @type {number} */\\nWebGLRenderingContext.GENERATE_MIPMAP_HINT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.BYTE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.UNSIGNED_BYTE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.SHORT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.UNSIGNED_SHORT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.INT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.UNSIGNED_INT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FLOAT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.DEPTH_COMPONENT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.ALPHA;\\n\\n/** @type {number} */\\nWebGLRenderingContext.RGB;\\n\\n/** @type {number} */\\nWebGLRenderingContext.RGBA;\\n\\n/** @type {number} */\\nWebGLRenderingContext.LUMINANCE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.LUMINANCE_ALPHA;\\n\\n/** @type {number} */\\nWebGLRenderingContext.UNSIGNED_SHORT_4_4_4_4;\\n\\n/** @type {number} */\\nWebGLRenderingContext.UNSIGNED_SHORT_5_5_5_1;\\n\\n/** @type {number} */\\nWebGLRenderingContext.UNSIGNED_SHORT_5_6_5;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FRAGMENT_SHADER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.VERTEX_SHADER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.MAX_VERTEX_ATTRIBS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.MAX_VERTEX_UNIFORM_VECTORS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.MAX_VARYING_VECTORS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.MAX_COMBINED_TEXTURE_IMAGE_UNITS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.MAX_VERTEX_TEXTURE_IMAGE_UNITS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.MAX_TEXTURE_IMAGE_UNITS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.MAX_FRAGMENT_UNIFORM_VECTORS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.SHADER_TYPE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.DELETE_STATUS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.LINK_STATUS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.VALIDATE_STATUS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.ATTACHED_SHADERS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.ACTIVE_UNIFORMS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.ACTIVE_ATTRIBUTES;\\n\\n/** @type {number} */\\nWebGLRenderingContext.SHADING_LANGUAGE_VERSION;\\n\\n/** @type {number} */\\nWebGLRenderingContext.CURRENT_PROGRAM;\\n\\n/** @type {number} */\\nWebGLRenderingContext.NEVER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.LESS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.EQUAL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.LEQUAL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.GREATER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.NOTEQUAL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.GEQUAL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.ALWAYS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.KEEP;\\n\\n/** @type {number} */\\nWebGLRenderingContext.REPLACE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.INCR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.DECR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.INVERT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.INCR_WRAP;\\n\\n/** @type {number} */\\nWebGLRenderingContext.DECR_WRAP;\\n\\n/** @type {number} */\\nWebGLRenderingContext.VENDOR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.RENDERER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.VERSION;\\n\\n/** @type {number} */\\nWebGLRenderingContext.NEAREST;\\n\\n/** @type {number} */\\nWebGLRenderingContext.LINEAR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.NEAREST_MIPMAP_NEAREST;\\n\\n/** @type {number} */\\nWebGLRenderingContext.LINEAR_MIPMAP_NEAREST;\\n\\n/** @type {number} */\\nWebGLRenderingContext.NEAREST_MIPMAP_LINEAR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.LINEAR_MIPMAP_LINEAR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE_MAG_FILTER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE_MIN_FILTER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE_WRAP_S;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE_WRAP_T;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE_2D;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE_CUBE_MAP;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE_BINDING_CUBE_MAP;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE_CUBE_MAP_POSITIVE_X;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE_CUBE_MAP_NEGATIVE_X;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE_CUBE_MAP_POSITIVE_Y;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE_CUBE_MAP_NEGATIVE_Y;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE_CUBE_MAP_POSITIVE_Z;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE_CUBE_MAP_NEGATIVE_Z;\\n\\n/** @type {number} */\\nWebGLRenderingContext.MAX_CUBE_MAP_TEXTURE_SIZE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE0;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE1;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE2;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE3;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE4;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE5;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE6;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE7;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE8;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE9;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE10;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE11;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE12;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE13;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE14;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE15;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE16;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE17;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE18;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE19;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE20;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE21;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE22;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE23;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE24;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE25;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE26;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE27;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE28;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE29;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE30;\\n\\n/** @type {number} */\\nWebGLRenderingContext.TEXTURE31;\\n\\n/** @type {number} */\\nWebGLRenderingContext.ACTIVE_TEXTURE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.REPEAT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.CLAMP_TO_EDGE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.MIRRORED_REPEAT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FLOAT_VEC2;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FLOAT_VEC3;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FLOAT_VEC4;\\n\\n/** @type {number} */\\nWebGLRenderingContext.INT_VEC2;\\n\\n/** @type {number} */\\nWebGLRenderingContext.INT_VEC3;\\n\\n/** @type {number} */\\nWebGLRenderingContext.INT_VEC4;\\n\\n/** @type {number} */\\nWebGLRenderingContext.BOOL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.BOOL_VEC2;\\n\\n/** @type {number} */\\nWebGLRenderingContext.BOOL_VEC3;\\n\\n/** @type {number} */\\nWebGLRenderingContext.BOOL_VEC4;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FLOAT_MAT2;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FLOAT_MAT3;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FLOAT_MAT4;\\n\\n/** @type {number} */\\nWebGLRenderingContext.SAMPLER_2D;\\n\\n/** @type {number} */\\nWebGLRenderingContext.SAMPLER_CUBE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.VERTEX_ATTRIB_ARRAY_ENABLED;\\n\\n/** @type {number} */\\nWebGLRenderingContext.VERTEX_ATTRIB_ARRAY_SIZE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.VERTEX_ATTRIB_ARRAY_STRIDE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.VERTEX_ATTRIB_ARRAY_TYPE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.VERTEX_ATTRIB_ARRAY_NORMALIZED;\\n\\n/** @type {number} */\\nWebGLRenderingContext.VERTEX_ATTRIB_ARRAY_POINTER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGLRenderingContext.IMPLEMENTATION_COLOR_READ_FORMAT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.IMPLEMENTATION_COLOR_READ_TYPE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.COMPILE_STATUS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.LOW_FLOAT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.MEDIUM_FLOAT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.HIGH_FLOAT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.LOW_INT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.MEDIUM_INT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.HIGH_INT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FRAMEBUFFER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.RENDERBUFFER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.RGBA4;\\n\\n/** @type {number} */\\nWebGLRenderingContext.RGB5_A1;\\n\\n/** @type {number} */\\nWebGLRenderingContext.RGB565;\\n\\n/** @type {number} */\\nWebGLRenderingContext.DEPTH_COMPONENT16;\\n\\n/** @type {number} */\\nWebGLRenderingContext.STENCIL_INDEX;\\n\\n/** @type {number} */\\nWebGLRenderingContext.STENCIL_INDEX8;\\n\\n/** @type {number} */\\nWebGLRenderingContext.DEPTH_STENCIL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.RENDERBUFFER_WIDTH;\\n\\n/** @type {number} */\\nWebGLRenderingContext.RENDERBUFFER_HEIGHT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.RENDERBUFFER_INTERNAL_FORMAT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.RENDERBUFFER_RED_SIZE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.RENDERBUFFER_GREEN_SIZE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.RENDERBUFFER_BLUE_SIZE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.RENDERBUFFER_ALPHA_SIZE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.RENDERBUFFER_DEPTH_SIZE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.RENDERBUFFER_STENCIL_SIZE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.COLOR_ATTACHMENT0;\\n\\n/** @type {number} */\\nWebGLRenderingContext.DEPTH_ATTACHMENT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.STENCIL_ATTACHMENT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.DEPTH_STENCIL_ATTACHMENT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.NONE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FRAMEBUFFER_COMPLETE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FRAMEBUFFER_INCOMPLETE_ATTACHMENT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FRAMEBUFFER_INCOMPLETE_DIMENSIONS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FRAMEBUFFER_UNSUPPORTED;\\n\\n/** @type {number} */\\nWebGLRenderingContext.FRAMEBUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGLRenderingContext.RENDERBUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGLRenderingContext.MAX_RENDERBUFFER_SIZE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.INVALID_FRAMEBUFFER_OPERATION;\\n\\n/** @type {number} */\\nWebGLRenderingContext.UNPACK_FLIP_Y_WEBGL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.UNPACK_PREMULTIPLY_ALPHA_WEBGL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.CONTEXT_LOST_WEBGL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.UNPACK_COLORSPACE_CONVERSION_WEBGL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.BROWSER_DEFAULT_WEBGL;\\n\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.DEPTH_BUFFER_BIT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.STENCIL_BUFFER_BIT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.COLOR_BUFFER_BIT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.POINTS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.LINES;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.LINE_LOOP;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.LINE_STRIP;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TRIANGLES;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TRIANGLE_STRIP;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TRIANGLE_FAN;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.ZERO;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.ONE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.SRC_COLOR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.ONE_MINUS_SRC_COLOR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.SRC_ALPHA;\\n\\n/** @type {number} */\\nWebGLRe"; +a.a+="nderingContext.prototype.ONE_MINUS_SRC_ALPHA;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.DST_ALPHA;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.ONE_MINUS_DST_ALPHA;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.DST_COLOR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.ONE_MINUS_DST_COLOR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.SRC_ALPHA_SATURATE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FUNC_ADD;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.BLEND_EQUATION;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.BLEND_EQUATION_RGB;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.BLEND_EQUATION_ALPHA;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FUNC_SUBTRACT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FUNC_REVERSE_SUBTRACT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.BLEND_DST_RGB;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.BLEND_SRC_RGB;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.BLEND_DST_ALPHA;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.BLEND_SRC_ALPHA;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.CONSTANT_COLOR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.ONE_MINUS_CONSTANT_COLOR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.CONSTANT_ALPHA;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.ONE_MINUS_CONSTANT_ALPHA;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.BLEND_COLOR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.ARRAY_BUFFER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.ELEMENT_ARRAY_BUFFER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.ARRAY_BUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.ELEMENT_ARRAY_BUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.STREAM_DRAW;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.STATIC_DRAW;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.DYNAMIC_DRAW;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.BUFFER_SIZE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.BUFFER_USAGE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.CURRENT_VERTEX_ATTRIB;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FRONT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.BACK;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FRONT_AND_BACK;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.CULL_FACE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.BLEND;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.DITHER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.STENCIL_TEST;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.DEPTH_TEST;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.SCISSOR_TEST;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.POLYGON_OFFSET_FILL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.SAMPLE_ALPHA_TO_COVERAGE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.SAMPLE_COVERAGE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.NO_ERROR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.INVALID_ENUM;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.INVALID_VALUE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.INVALID_OPERATION;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.OUT_OF_MEMORY;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.CW;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.CCW;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.LINE_WIDTH;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.ALIASED_POINT_SIZE_RANGE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.ALIASED_LINE_WIDTH_RANGE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.CULL_FACE_MODE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FRONT_FACE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.DEPTH_RANGE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.DEPTH_WRITEMASK;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.DEPTH_CLEAR_VALUE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.DEPTH_FUNC;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.STENCIL_CLEAR_VALUE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.STENCIL_FUNC;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.STENCIL_FAIL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.STENCIL_PASS_DEPTH_FAIL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.STENCIL_PASS_DEPTH_PASS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.STENCIL_REF;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.STENCIL_VALUE_MASK;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.STENCIL_WRITEMASK;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.STENCIL_BACK_FUNC;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.STENCIL_BACK_FAIL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.STENCIL_BACK_PASS_DEPTH_FAIL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.STENCIL_BACK_PASS_DEPTH_PASS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.STENCIL_BACK_REF;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.STENCIL_BACK_VALUE_MASK;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.STENCIL_BACK_WRITEMASK;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.VIEWPORT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.SCISSOR_BOX;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.COLOR_CLEAR_VALUE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.COLOR_WRITEMASK;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.UNPACK_ALIGNMENT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.PACK_ALIGNMENT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.MAX_TEXTURE_SIZE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.MAX_VIEWPORT_DIMS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.SUBPIXEL_BITS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.RED_BITS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.GREEN_BITS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.BLUE_BITS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.ALPHA_BITS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.DEPTH_BITS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.STENCIL_BITS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.POLYGON_OFFSET_UNITS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.POLYGON_OFFSET_FACTOR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE_BINDING_2D;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.SAMPLE_BUFFERS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.SAMPLES;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.SAMPLE_COVERAGE_VALUE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.SAMPLE_COVERAGE_INVERT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.COMPRESSED_TEXTURE_FORMATS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.DONT_CARE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FASTEST;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.NICEST;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.GENERATE_MIPMAP_HINT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.BYTE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.UNSIGNED_BYTE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.SHORT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.UNSIGNED_SHORT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.INT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.UNSIGNED_INT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FLOAT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.DEPTH_COMPONENT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.ALPHA;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.RGB;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.RGBA;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.LUMINANCE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.LUMINANCE_ALPHA;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.UNSIGNED_SHORT_4_4_4_4;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.UNSIGNED_SHORT_5_5_5_1;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.UNSIGNED_SHORT_5_6_5;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FRAGMENT_SHADER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.VERTEX_SHADER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.MAX_VERTEX_ATTRIBS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.MAX_VERTEX_UNIFORM_VECTORS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.MAX_VARYING_VECTORS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.MAX_COMBINED_TEXTURE_IMAGE_UNITS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.MAX_VERTEX_TEXTURE_IMAGE_UNITS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.MAX_TEXTURE_IMAGE_UNITS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.MAX_FRAGMENT_UNIFORM_VECTORS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.SHADER_TYPE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.DELETE_STATUS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.LINK_STATUS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.VALIDATE_STATUS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.ATTACHED_SHADERS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.ACTIVE_UNIFORMS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.ACTIVE_ATTRIBUTES;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.SHADING_LANGUAGE_VERSION;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.CURRENT_PROGRAM;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.NEVER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.LESS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.EQUAL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.LEQUAL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.GREATER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.NOTEQUAL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.GEQUAL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.ALWAYS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.KEEP;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.REPLACE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.INCR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.DECR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.INVERT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.INCR_WRAP;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.DECR_WRAP;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.VENDOR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.RENDERER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.VERSION;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.NEAREST;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.LINEAR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.NEAREST_MIPMAP_NEAREST;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.LINEAR_MIPMAP_NEAREST;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.NEAREST_MIPMAP_LINEAR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.LINEAR_MIPMAP_LINEAR;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE_MAG_FILTER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE_MIN_FILTER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE_WRAP_S;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE_WRAP_T;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE_2D;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE_CUBE_MAP;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE_BINDING_CUBE_MAP;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE_CUBE_MAP_POSITIVE_X;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE_CUBE_MAP_NEGATIVE_X;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE_CUBE_MAP_POSITIVE_Y;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE_CUBE_MAP_NEGATIVE_Y;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE_CUBE_MAP_POSITIVE_Z;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE_CUBE_MAP_NEGATIVE_Z;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.MAX_CUBE_MAP_TEXTURE_SIZE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE0;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE1;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE2;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE3;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE4;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE5;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE6;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE7;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE8;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE9;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE10;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE11;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE12;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE13;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE14;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE15;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE16;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE17;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE18;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE19;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE20;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE21;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE22;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE23;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE24;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE25;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE26;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE27;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE28;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE29;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE30;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.TEXTURE31;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.ACTIVE_TEXTURE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.REPEAT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.CLAMP_TO_EDGE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.MIRRORED_REPEAT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FLOAT_VEC2;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FLOAT_VEC3;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FLOAT_VEC4;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.INT_VEC2;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.INT_VEC3;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.INT_VEC4;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.BOOL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.BOOL_VEC2;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.BOOL_VEC3;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.BOOL_VEC4;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FLOAT_MAT2;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FLOAT_MAT3;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FLOAT_MAT4;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.SAMPLER_2D;\\n\\n/** @"; +a.a+="type {number} */\\nWebGLRenderingContext.prototype.SAMPLER_CUBE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.VERTEX_ATTRIB_ARRAY_ENABLED;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.VERTEX_ATTRIB_ARRAY_SIZE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.VERTEX_ATTRIB_ARRAY_STRIDE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.VERTEX_ATTRIB_ARRAY_TYPE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.VERTEX_ATTRIB_ARRAY_NORMALIZED;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.VERTEX_ATTRIB_ARRAY_POINTER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.COMPILE_STATUS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.LOW_FLOAT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.MEDIUM_FLOAT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.HIGH_FLOAT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.LOW_INT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.MEDIUM_INT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.HIGH_INT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FRAMEBUFFER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.RENDERBUFFER;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.RGBA4;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.RGB5_A1;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.RGB565;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.DEPTH_COMPONENT16;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.STENCIL_INDEX;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.STENCIL_INDEX8;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.DEPTH_STENCIL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.RENDERBUFFER_WIDTH;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.RENDERBUFFER_HEIGHT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.RENDERBUFFER_INTERNAL_FORMAT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.RENDERBUFFER_RED_SIZE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.RENDERBUFFER_GREEN_SIZE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.RENDERBUFFER_BLUE_SIZE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.RENDERBUFFER_ALPHA_SIZE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.RENDERBUFFER_DEPTH_SIZE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.RENDERBUFFER_STENCIL_SIZE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.COLOR_ATTACHMENT0;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.DEPTH_ATTACHMENT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.STENCIL_ATTACHMENT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.DEPTH_STENCIL_ATTACHMENT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.NONE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FRAMEBUFFER_COMPLETE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FRAMEBUFFER_INCOMPLETE_ATTACHMENT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FRAMEBUFFER_INCOMPLETE_DIMENSIONS;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FRAMEBUFFER_UNSUPPORTED;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.FRAMEBUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.RENDERBUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.MAX_RENDERBUFFER_SIZE;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.INVALID_FRAMEBUFFER_OPERATION;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.UNPACK_FLIP_Y_WEBGL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.UNPACK_PREMULTIPLY_ALPHA_WEBGL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.CONTEXT_LOST_WEBGL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.UNPACK_COLORSPACE_CONVERSION_WEBGL;\\n\\n/** @type {number} */\\nWebGLRenderingContext.prototype.BROWSER_DEFAULT_WEBGL;\\n\\n\\n/**\\n * @type {!HTMLCanvasElement}\\n */\\nWebGLRenderingContext.prototype.canvas;\\n\\n/**\\n * @type {number}\\n */\\nWebGLRenderingContext.prototype.drawingBufferWidth;\\n\\n/**\\n * @type {number}\\n */\\nWebGLRenderingContext.prototype.drawingBufferHeight;\\n\\n/**\\n * @return {!WebGLContextAttributes}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.getContextAttributes = function() {};\\n\\n/**\\n * @return {boolean}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.isContextLost = function() {};\\n\\n/**\\n * @return {!Array}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.getSupportedExtensions = function() {};\\n\\n/**\\n * Note that this has side effects by enabling the extension even if the\\n * result is not used.\\n * @param {string} name\\n * @return {Object}\\n */\\nWebGLRenderingContext.prototype.getExtension = function(name) {};\\n\\n/**\\n * @param {number} texture\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.activeTexture = function(texture) {};\\n\\n/**\\n * @param {WebGLProgram} program\\n * @param {WebGLShader} shader\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.attachShader = function(program, shader) {};\\n\\n/**\\n * @param {WebGLProgram} program\\n * @param {number} index\\n * @param {string} name\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.bindAttribLocation = function(\\n program, index, name) {};\\n\\n/**\\n * @param {number} target\\n * @param {WebGLBuffer} buffer\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.bindBuffer = function(target, buffer) {};\\n\\n/**\\n * @param {number} target\\n * @param {WebGLFramebuffer} buffer\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.bindFramebuffer = function(target, buffer) {};\\n\\n/**\\n * @param {number} target\\n * @param {WebGLRenderbuffer} buffer\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.bindRenderbuffer = function(target, buffer) {};\\n\\n/**\\n * @param {number} target\\n * @param {WebGLTexture} texture\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.bindTexture = function(target, texture) {};\\n\\n/**\\n * @param {number} red\\n * @param {number} green\\n * @param {number} blue\\n * @param {number} alpha\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.blendColor = function(\\n red, green, blue, alpha) {};\\n\\n/**\\n * @param {number} mode\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.blendEquation = function(mode) {};\\n\\n/**\\n * @param {number} modeRGB\\n * @param {number} modeAlpha\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.blendEquationSeparate = function(\\n modeRGB, modeAlpha) {};\\n\\n/**\\n * @param {number} sfactor\\n * @param {number} dfactor\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.blendFunc = function(sfactor, dfactor) {};\\n\\n/**\\n * @param {number} srcRGB\\n * @param {number} dstRGB\\n * @param {number} srcAlpha\\n * @param {number} dstAlpha\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.blendFuncSeparate = function(\\n srcRGB, dstRGB, srcAlpha, dstAlpha) {};\\n\\n/**\\n * @param {number} target\\n * @param {ArrayBufferView|ArrayBuffer|number} data\\n * @param {number} usage\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.bufferData = function(target, data, usage) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} offset\\n * @param {ArrayBufferView|ArrayBuffer} data\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.bufferSubData = function(\\n target, offset, data) {};\\n\\n/**\\n * @param {number} target\\n * @return {number}\\n */\\nWebGLRenderingContext.prototype.checkFramebufferStatus = function(target) {};\\n\\n/**\\n * @param {number} mask\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.clear = function(mask) {};\\n\\n/**\\n * @param {number} red\\n * @param {number} green\\n * @param {number} blue\\n * @param {number} alpha\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.clearColor = function(\\n red, green, blue, alpha) {};\\n\\n/**\\n * @param {number} depth\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.clearDepth = function(depth) {};\\n\\n/**\\n * @param {number} s\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.clearStencil = function(s) {};\\n\\n/**\\n * @param {boolean} red\\n * @param {boolean} green\\n * @param {boolean} blue\\n * @param {boolean} alpha\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.colorMask = function(\\n red, green, blue, alpha) {};\\n\\n/**\\n * @param {WebGLShader} shader\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.compileShader = function(shader) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} level\\n * @param {number} internalformat\\n * @param {number} width\\n * @param {number} height\\n * @param {number} border\\n * @param {ArrayBufferView} data\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.compressedTexImage2D = function(\\n target, level, internalformat, width, height, border, data) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} level\\n * @param {number} xoffset\\n * @param {number} yoffset\\n * @param {number} width\\n * @param {number} height\\n * @param {number} format\\n * @param {ArrayBufferView} data\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.compressedTexSubImage2D = function(\\n target, level, xoffset, yoffset, width, height, format, data) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} level\\n * @param {number} format\\n * @param {number} x\\n * @param {number} y\\n * @param {number} width\\n * @param {number} height\\n * @param {number} border\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.copyTexImage2D = function(\\n target, level, format, x, y, width, height, border) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} level\\n * @param {number} xoffset\\n * @param {number} yoffset\\n * @param {number} x\\n * @param {number} y\\n * @param {number} width\\n * @param {number} height\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.copyTexSubImage2D = function(\\n target, level, xoffset, yoffset, x, y, width, height) {};\\n\\n/**\\n * @return {!WebGLBuffer}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.createBuffer = function() {};\\n\\n/**\\n * @return {!WebGLFramebuffer}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.createFramebuffer = function() {};\\n\\n/**\\n * @return {!WebGLProgram}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.createProgram = function() {};\\n\\n/**\\n * @return {!WebGLRenderbuffer}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.createRenderbuffer = function() {};\\n\\n/**\\n * @param {number} type\\n * @return {!WebGLShader}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.createShader = function(type) {};\\n\\n/**\\n * @return {!WebGLTexture}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.createTexture = function() {};\\n\\n/**\\n * @param {number} mode\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.cullFace = function(mode) {};\\n\\n/**\\n * @param {WebGLBuffer} buffer\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.deleteBuffer = function(buffer) {};\\n\\n/**\\n * @param {WebGLFramebuffer} buffer\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.deleteFramebuffer = function(buffer) {};\\n\\n/**\\n * @param {WebGLProgram} program\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.deleteProgram = function(program) {};\\n\\n/**\\n * @param {WebGLRenderbuffer} buffer\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.deleteRenderbuffer = function(buffer) {};\\n\\n/**\\n * @param {WebGLShader} shader\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.deleteShader = function(shader) {};\\n\\n/**\\n * @param {WebGLTexture} texture\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.deleteTexture = function(texture) {};\\n\\n/**\\n * @param {number} func\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.depthFunc = function(func) {};\\n\\n/**\\n * @param {boolean} flag\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.depthMask = function(flag) {};\\n\\n/**\\n * @param {number} nearVal\\n * @param {number} farVal\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.depthRange = function(nearVal, farVal) {};\\n\\n/**\\n * @param {WebGLProgram} program\\n * @param {WebGLShader} shader\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.detachShader = function(program, shader) {};\\n\\n/**\\n * @param {number} flags\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.disable = function(flags) {};\\n\\n/**\\n * @param {number} index\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.disableVertexAttribArray = function(\\n index) {};\\n\\n/**\\n * @param {number} mode\\n * @param {number} first\\n * @param {number} count\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.drawArrays = function(mode, first, count) {};\\n\\n/**\\n * @param {number} mode\\n * @param {number} count\\n * @param {number} type\\n * @param {number} offset\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.drawElements = function(\\n mode, count, type, offset) {};\\n\\n/**\\n * @param {number} cap\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.enable = function(cap) {};\\n\\n/**\\n * @param {number} index\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.enableVertexAttribArray = function(\\n index) {};\\n\\nWebGLRenderingContext.prototype.finish = function() {};\\n\\nWebGLRenderingContext.prototype.flush = function() {};\\n\\n/**\\n * @param {number} target\\n * @param {number} attachment\\n * @param {number} renderbuffertarget\\n * @param {WebGLRenderbuffer} renderbuffer\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.framebufferRenderbuffer = function(\\n target, attachment, renderbuffertarget, renderbuffer) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} attachment\\n * @param {number} textarget\\n * @param {WebGLTexture} texture\\n * @param {number} level\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.framebufferTexture2D = function(\\n target, attachment, textarget, texture, level) {};\\n\\n/**\\n * @param {number} mode\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.frontFace = function(mode) {};\\n\\n/**\\n * @param {number} target\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.generateMipmap = function(target) {};\\n\\n/**\\n * @param {WebGLProgram} program\\n * @param {number} index\\n * @return {WebGLActiveInfo}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.getActiveAttrib = function(program, index) {};\\n\\n/**\\n * @param {WebGLProgram} program\\n * @param {number} index\\n * @return {WebGLActiveInfo}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.getActiveUniform = function(program, index) {};\\n\\n/**\\n * @param {WebGLProgram} program\\n * @return {!Array}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.getAttachedShaders = function(program) {};\\n\\n/**\\n * @param {WebGLProgram} program\\n * @param {string} name\\n * @return {number}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.getAttribLocation = function(program, name) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} pname\\n * @return {*}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.getBufferParameter = function(target, pname) {};\\n\\n/**\\n * @param {number} pname\\n * @return {*}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.getParameter = function(pname) {};\\n\\n/**\\n * @return {number}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.getError = function() {};\\n\\n/**\\n * @param {number} target\\n * @param {number} attachment\\n * @param {number} pname\\n * @return {*}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.getFramebufferAttachmentParameter = function(\\n target, at"; +a.a+="tachment, pname) {};\\n\\n/**\\n * @param {WebGLProgram} program\\n * @param {number} pname\\n * @return {*}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.getProgramParameter = function(\\n program, pname) {};\\n\\n/**\\n * @param {WebGLProgram} program\\n * @return {string}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.getProgramInfoLog = function(program) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} pname\\n * @return {*}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.getRenderbufferParameter = function(\\n target, pname) {};\\n\\n/**\\n * @param {WebGLShader} shader\\n * @param {number} pname\\n * @return {*}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.getShaderParameter = function(shader, pname) {};\\n\\n/**\\n * @param {number} shadertype\\n * @param {number} precisiontype\\n * @return {WebGLShaderPrecisionFormat}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.getShaderPrecisionFormat = function(shadertype,\\n precisiontype) {};\\n\\n/**\\n * @param {WebGLShader} shader\\n * @return {string}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.getShaderInfoLog = function(shader) {};\\n\\n/**\\n * @param {WebGLShader} shader\\n * @return {string}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.getShaderSource = function(shader) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} pname\\n * @return {*}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.getTexParameter = function(target, pname) {};\\n\\n/**\\n * @param {WebGLProgram} program\\n * @param {WebGLUniformLocation} location\\n * @return {*}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.getUniform = function(program, location) {};\\n\\n/**\\n * @param {WebGLProgram} program\\n * @param {string} name\\n * @return {WebGLUniformLocation}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.getUniformLocation = function(program, name) {};\\n\\n/**\\n * @param {number} index\\n * @param {number} pname\\n * @return {*}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.getVertexAttrib = function(index, pname) {};\\n\\n/**\\n * @param {number} index\\n * @param {number} pname\\n * @return {number}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.getVertexAttribOffset = function(\\n index, pname) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} mode\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.hint = function(target, mode) {};\\n\\n/**\\n * @param {WebGLObject} buffer\\n * @return {boolean}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.isBuffer = function(buffer) {};\\n\\n/**\\n * @param {number} cap\\n * @return {boolean}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.isEnabled = function(cap) {};\\n\\n/**\\n * @param {WebGLObject} framebuffer\\n * @return {boolean}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.isFramebuffer = function(framebuffer) {};\\n\\n/**\\n * @param {WebGLObject} program\\n * @return {boolean}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.isProgram = function(program) {};\\n\\n/**\\n * @param {WebGLObject} renderbuffer\\n * @return {boolean}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.isRenderbuffer = function(renderbuffer) {};\\n\\n/**\\n * @param {WebGLObject} shader\\n * @return {boolean}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.isShader = function(shader) {};\\n\\n/**\\n * @param {WebGLObject} texture\\n * @return {boolean}\\n * @nosideeffects\\n */\\nWebGLRenderingContext.prototype.isTexture = function(texture) {};\\n\\n/**\\n * @param {number} width\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.lineWidth = function(width) {};\\n\\n/**\\n * @param {WebGLProgram} program\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.linkProgram = function(program) {};\\n\\n/**\\n * @param {number} pname\\n * @param {number|boolean} param\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.pixelStorei = function(pname, param) {};\\n\\n/**\\n * @param {number} factor\\n * @param {number} units\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.polygonOffset = function(factor, units) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @param {number} width\\n * @param {number} height\\n * @param {number} format\\n * @param {number} type\\n * @param {ArrayBufferView} pixels\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.readPixels = function(\\n x, y, width, height, format, type, pixels) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} internalformat\\n * @param {number} width\\n * @param {number} height\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.renderbufferStorage = function(\\n target, internalformat, width, height) {};\\n\\n/**\\n * @param {number} coverage\\n * @param {boolean} invert\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.sampleCoverage = function(coverage, invert) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @param {number} width\\n * @param {number} height\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.scissor = function(x, y, width, height) {};\\n\\n/**\\n * @param {WebGLShader} shader\\n * @param {string} source\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.shaderSource = function(shader, source) {};\\n\\n/**\\n * @param {number} func\\n * @param {number} ref\\n * @param {number} mask\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.stencilFunc = function(func, ref, mask) {};\\n\\n/**\\n * @param {number} face\\n * @param {number} func\\n * @param {number} ref\\n * @param {number} mask\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.stencilFuncSeparate = function(\\n face, func, ref, mask) {};\\n\\n/**\\n * @param {number} mask\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.stencilMask = function(mask) {};\\n\\n/**\\n * @param {number} face\\n * @param {number} mask\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.stencilMaskSeparate = function(face, mask) {};\\n\\n/**\\n * @param {number} fail\\n * @param {number} zfail\\n * @param {number} zpass\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.stencilOp = function(fail, zfail, zpass) {};\\n\\n/**\\n * @param {number} face\\n * @param {number} fail\\n * @param {number} zfail\\n * @param {number} zpass\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.stencilOpSeparate = function(\\n face, fail, zfail, zpass) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} level\\n * @param {number} internalformat\\n * @param {number} format or width\\n * @param {number} type or height\\n * @param {?TexImageSource|number} img or border\\n * @param {number=} opt_format\\n * @param {number=} opt_type\\n * @param {ArrayBufferView=} opt_pixels\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.texImage2D = function(\\n target, level, internalformat, format, type, img, opt_format, opt_type,\\n opt_pixels) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} pname\\n * @param {number} param\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.texParameterf = function(\\n target, pname, param) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} pname\\n * @param {number} param\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.texParameteri = function(\\n target, pname, param) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} level\\n * @param {number} xoffset\\n * @param {number} yoffset\\n * @param {number} format or width\\n * @param {number} type or height\\n * @param {?TexImageSource|number} data or format\\n * @param {number=} opt_type\\n * @param {ArrayBufferView=} opt_pixels\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.texSubImage2D = function(\\n target, level, xoffset, yoffset, format, type, data, opt_type,\\n opt_pixels) {};\\n\\n/**\\n * @param {WebGLUniformLocation} location\\n * @param {number} value\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.uniform1f = function(location, value) {};\\n\\n/**\\n * @param {WebGLUniformLocation} location\\n * @param {Float32Array|Array} value\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.uniform1fv = function(location, value) {};\\n\\n/**\\n * @param {WebGLUniformLocation} location\\n * @param {number|boolean} value\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.uniform1i = function(location, value) {};\\n\\n/**\\n * @param {WebGLUniformLocation} location\\n * @param {Int32Array|Array|Array} value\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.uniform1iv = function(location, value) {};\\n\\n/**\\n * @param {WebGLUniformLocation} location\\n * @param {number} value1\\n * @param {number} value2\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.uniform2f = function(\\n location, value1, value2) {};\\n\\n/**\\n * @param {WebGLUniformLocation} location\\n * @param {Float32Array|Array} value\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.uniform2fv = function(location, value) {};\\n\\n/**\\n * @param {WebGLUniformLocation} location\\n * @param {number|boolean} value1\\n * @param {number|boolean} value2\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.uniform2i = function(\\n location, value1, value2) {};\\n\\n/**\\n * @param {WebGLUniformLocation} location\\n * @param {Int32Array|Array|Array} value\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.uniform2iv = function(location, value) {};\\n\\n/**\\n * @param {WebGLUniformLocation} location\\n * @param {number} value1\\n * @param {number} value2\\n * @param {number} value3\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.uniform3f = function(\\n location, value1, value2, value3) {};\\n\\n/**\\n * @param {WebGLUniformLocation} location\\n * @param {Float32Array|Array} value\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.uniform3fv = function(location, value) {};\\n\\n/**\\n * @param {WebGLUniformLocation} location\\n * @param {number|boolean} value1\\n * @param {number|boolean} value2\\n * @param {number|boolean} value3\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.uniform3i = function(\\n location, value1, value2, value3) {};\\n\\n/**\\n * @param {WebGLUniformLocation} location\\n * @param {Int32Array|Array|Array} value\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.uniform3iv = function(location, value) {};\\n\\n/**\\n * @param {WebGLUniformLocation} location\\n * @param {number} value1\\n * @param {number} value2\\n * @param {number} value3\\n * @param {number} value4\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.uniform4f = function(\\n location, value1, value2, value3, value4) {};\\n\\n/**\\n * @param {WebGLUniformLocation} location\\n * @param {Float32Array|Array} value\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.uniform4fv = function(location, value) {};\\n\\n/**\\n * @param {WebGLUniformLocation} location\\n * @param {number|boolean} value1\\n * @param {number|boolean} value2\\n * @param {number|boolean} value3\\n * @param {number|boolean} value4\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.uniform4i = function(\\n location, value1, value2, value3, value4) {};\\n\\n/**\\n * @param {WebGLUniformLocation} location\\n * @param {Int32Array|Array|Array} value\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.uniform4iv = function(location, value) {};\\n\\n/**\\n * @param {WebGLUniformLocation} location\\n * @param {boolean} transpose\\n * @param {Float32Array|Array} data\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.uniformMatrix2fv = function(\\n location, transpose, data) {};\\n\\n/**\\n * @param {WebGLUniformLocation} location\\n * @param {boolean} transpose\\n * @param {Float32Array|Array} data\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.uniformMatrix3fv = function(\\n location, transpose, data) {};\\n\\n/**\\n * @param {WebGLUniformLocation} location\\n * @param {boolean} transpose\\n * @param {Float32Array|Array} data\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.uniformMatrix4fv = function(\\n location, transpose, data) {};\\n\\n/**\\n * @param {WebGLProgram} program\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.useProgram = function(program) {};\\n\\n/**\\n * @param {WebGLProgram} program\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.validateProgram = function(program) {};\\n\\n/**\\n * @param {number} indx\\n * @param {number} x\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.vertexAttrib1f = function(indx, x) {};\\n\\n/**\\n * @param {number} indx\\n * @param {Float32Array|Array} values\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.vertexAttrib1fv = function(indx, values) {};\\n\\n/**\\n * @param {number} indx\\n * @param {number} x\\n * @param {number} y\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.vertexAttrib2f = function(\\n indx, x, y) {};\\n\\n/**\\n * @param {number} indx\\n * @param {Float32Array|Array} values\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.vertexAttrib2fv = function(\\n indx, values) {};\\n\\n/**\\n * @param {number} indx\\n * @param {number} x\\n * @param {number} y\\n * @param {number} z\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.vertexAttrib3f = function(\\n indx, x, y, z) {};\\n\\n/**\\n * @param {number} indx\\n * @param {Float32Array|Array} values\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.vertexAttrib3fv = function(indx, values) {};\\n\\n/**\\n * @param {number} indx\\n * @param {number} x\\n * @param {number} y\\n * @param {number} z\\n * @param {number} w\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.vertexAttrib4f = function(\\n indx, x, y, z, w) {};\\n\\n/**\\n * @param {number} indx\\n * @param {Float32Array|Array} values\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.vertexAttrib4fv = function(indx, values) {};\\n\\n/**\\n * @param {number} indx\\n * @param {number} size\\n * @param {number} type\\n * @param {boolean} normalized\\n * @param {number} stride\\n * @param {number} offset\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.vertexAttribPointer = function(\\n indx, size, type, normalized, stride, offset) {};\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @param {number} width\\n * @param {number} height\\n * @return {undefined}\\n */\\nWebGLRenderingContext.prototype.viewport = function(x, y, width, height) {};\\n\\n\\n/**\\n * @constructor\\n */\\nfunction WebGLContextAttributes() {}\\n\\n/**\\n * @type {boolean}\\n */\\nWebGLContextAttributes.prototype.alpha;\\n\\n/**\\n * @type {boolean}\\n */\\nWebGLContextAttributes.prototype.depth;\\n\\n/**\\n * @type {boolean}\\n */\\nWebGLContextAttributes.prototype.stencil;\\n\\n/**\\n * @type {boolean}\\n */\\nWebGLContextAttributes.prototype.antialias;\\n\\n/**\\n * @type {boolean}\\n */\\nWebGLContextAttributes.prototype.premultipliedAlpha;\\n\\n/**\\n * @type {boolean}\\n */\\nWebGLContextAttributes.prototype.preserveDrawingBuffer;\\n\\n/**\\n * @type {boolean}\\n */\\nWebGLContextAttributes.prototype.preferLowPowerToHighPerformance;\\n\\n/**\\n * @type {boolean}\\n */\\nWebGLContextAttributes.prototype.failIfMajorPerformanceCaveat;\\n\\n/**\\n * @param {string} eventType\\n * @constructor\\n * @extends {Event}\\n */\\nfunction WebGLContextEvent(eventType) {}\\n\\n/**\\n * @type {string}\\n */\\nWebGLContextEvent.prototype.statusMessage;\\n\\n\\n/**\\n * @constructor\\n */\\nfunction WebGLShaderPrecisionFormat() {}\\n\\n/**\\n * @type {number}\\n */\\nWebGLShaderPrecisionFormat.prototype.rangeMin;\\n\\n/**\\n * @type {number}\\n */\\nWebGLShaderPrecisionFormat.prototype.rangeMax;\\n\\n/**\\n * @type {number}\\n */\\nWebGLShaderPrecisionFormat.prototype.precision;\\n\\n\\n/**\\n * @constructor\\n */\\nfunction WebGLObject() {}\\n\\n\\n/**\\n * @constructor\\n * @extends {WebGLObject}\\n */\\nfunction WebGLBuffer() {}\\n\\n\\n/**\\n * @constructor\\n * @extends {WebGLObject}\\n */\\nfunction WebGLFramebuffer() {}\\n\\n\\n/**\\n * @constructor\\n * @extends {WebGLObject}\\n */\\nfunction WebGLProgram() {}\\n\\n\\n/**\\n * @constructor\\n * @extends {WebGLObject}\\n */\\nfunction WebGLRenderbuffer() {}\\n\\n\\n/**\\n * @constructor\\n * @extends {WebGLObject}\\n */\\nfunction WebGLShader() {}\\n\\n\\n/**\\n * @constructor\\n * @extends {WebGLObject}\\n */\\nfunction WebGLTexture() {}\\n\\n\\n/*"; +a.a+='*\\n * @constructor\\n */\\nfunction WebGLActiveInfo() {}\\n\\n/** @type {number} */\\nWebGLActiveInfo.prototype.size;\\n\\n/** @type {number} */\\nWebGLActiveInfo.prototype.type;\\n\\n/** @type {string} */\\nWebGLActiveInfo.prototype.name;\\n\\n\\n/**\\n * @constructor\\n */\\nfunction WebGLUniformLocation() {}\\n\\n\\n/**\\n * @see http://www.khronos.org/registry/webgl/extensions/OES_texture_float/\\n * @constructor\\n */\\nfunction OES_texture_float() {}\\n\\n\\n/**\\n * @see http://www.khronos.org/registry/webgl/extensions/OES_texture_half_float/\\n * @constructor\\n */\\nfunction OES_texture_half_float() {}\\n\\n/** @type {number} */\\nOES_texture_half_float.prototype.HALF_FLOAT_OES;\\n\\n\\n/**\\n * @see http://www.khronos.org/registry/webgl/extensions/WEBGL_lose_context/\\n * @constructor\\n */\\nfunction WEBGL_lose_context() {}\\n\\nWEBGL_lose_context.prototype.loseContext = function() {};\\n\\nWEBGL_lose_context.prototype.restoreContext = function() {};\\n\\n\\n/**\\n * @see http://www.khronos.org/registry/webgl/extensions/OES_standard_derivatives/\\n * @constructor\\n */\\nfunction OES_standard_derivatives() {}\\n\\n/** @type {number} */\\nOES_standard_derivatives.prototype.FRAGMENT_SHADER_DERIVATIVE_HINT_OES;\\n\\n\\n/**\\n * @see http://www.khronos.org/registry/webgl/extensions/OES_vertex_array_object/\\n * @constructor\\n * @extends {WebGLObject}\\n */\\nfunction WebGLVertexArrayObjectOES() {}\\n\\n\\n/**\\n * @see http://www.khronos.org/registry/webgl/extensions/OES_vertex_array_object/\\n * @constructor\\n */\\nfunction OES_vertex_array_object() {}\\n\\n/** @type {number} */\\nOES_vertex_array_object.prototype.VERTEX_ARRAY_BINDING_OES;\\n\\n/**\\n * @return {WebGLVertexArrayObjectOES}\\n * @nosideeffects\\n */\\nOES_vertex_array_object.prototype.createVertexArrayOES = function() {};\\n\\n/**\\n * @param {WebGLVertexArrayObjectOES} arrayObject\\n * @return {undefined}\\n */\\nOES_vertex_array_object.prototype.deleteVertexArrayOES =\\n function(arrayObject) {};\\n\\n/**\\n * @param {WebGLVertexArrayObjectOES} arrayObject\\n * @return {boolean}\\n * @nosideeffects\\n */\\nOES_vertex_array_object.prototype.isVertexArrayOES = function(arrayObject) {};\\n\\n/**\\n * @param {WebGLVertexArrayObjectOES} arrayObject\\n * @return {undefined}\\n */\\nOES_vertex_array_object.prototype.bindVertexArrayOES = function(arrayObject) {};\\n\\n\\n/**\\n * @see http://www.khronos.org/registry/webgl/extensions/WEBGL_debug_renderer_info/\\n * @constructor\\n */\\nfunction WEBGL_debug_renderer_info() {}\\n\\n/** @type {number} */\\nWEBGL_debug_renderer_info.prototype.UNMASKED_VENDOR_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_debug_renderer_info.prototype.UNMASKED_RENDERER_WEBGL;\\n\\n\\n/**\\n * @see http://www.khronos.org/registry/webgl/extensions/WEBGL_debug_shaders/\\n * @constructor\\n */\\nfunction WEBGL_debug_shaders() {}\\n\\n/**\\n * @param {WebGLShader} shader\\n * @return {string}\\n * @nosideeffects\\n */\\nWEBGL_debug_shaders.prototype.getTranslatedShaderSource = function(shader) {};\\n\\n\\n/**\\n * @see http://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/\\n * @constructor\\n */\\nfunction WEBGL_compressed_texture_s3tc() {}\\n\\n/** @type {number} */\\nWEBGL_compressed_texture_s3tc.prototype.COMPRESSED_RGB_S3TC_DXT1_EXT;\\n\\n/** @type {number} */\\nWEBGL_compressed_texture_s3tc.prototype.COMPRESSED_RGBA_S3TC_DXT1_EXT;\\n\\n/** @type {number} */\\nWEBGL_compressed_texture_s3tc.prototype.COMPRESSED_RGBA_S3TC_DXT3_EXT;\\n\\n/** @type {number} */\\nWEBGL_compressed_texture_s3tc.prototype.COMPRESSED_RGBA_S3TC_DXT5_EXT;\\n\\n\\n/**\\n * @see http://www.khronos.org/registry/webgl/extensions/OES_depth_texture/\\n * @constructor\\n */\\nfunction OES_depth_texture() {}\\n\\n\\n/**\\n * @see http://www.khronos.org/registry/webgl/extensions/OES_element_index_uint/\\n * @constructor\\n */\\nfunction OES_element_index_uint() {}\\n\\n\\n/**\\n * @see http://www.khronos.org/registry/webgl/extensions/EXT_texture_filter_anisotropic/\\n * @constructor\\n */\\nfunction EXT_texture_filter_anisotropic() {}\\n\\n/** @type {number} */\\nEXT_texture_filter_anisotropic.prototype.TEXTURE_MAX_ANISOTROPY_EXT;\\n\\n/** @type {number} */\\nEXT_texture_filter_anisotropic.prototype.MAX_TEXTURE_MAX_ANISOTROPY_EXT;\\n\\n\\n/**\\n * @see https://www.khronos.org/registry/webgl/extensions/WEBGL_draw_buffers/\\n * @constructor\\n */\\nfunction WEBGL_draw_buffers() {}\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.COLOR_ATTACHMENT0_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.COLOR_ATTACHMENT1_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.COLOR_ATTACHMENT2_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.COLOR_ATTACHMENT3_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.COLOR_ATTACHMENT4_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.COLOR_ATTACHMENT5_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.COLOR_ATTACHMENT6_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.COLOR_ATTACHMENT7_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.COLOR_ATTACHMENT8_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.COLOR_ATTACHMENT9_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.COLOR_ATTACHMENT10_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.COLOR_ATTACHMENT11_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.COLOR_ATTACHMENT12_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.COLOR_ATTACHMENT13_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.COLOR_ATTACHMENT14_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.COLOR_ATTACHMENT15_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.DRAW_BUFFER0_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.DRAW_BUFFER1_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.DRAW_BUFFER2_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.DRAW_BUFFER3_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.DRAW_BUFFER4_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.DRAW_BUFFER5_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.DRAW_BUFFER6_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.DRAW_BUFFER7_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.DRAW_BUFFER8_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.DRAW_BUFFER9_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.DRAW_BUFFER10_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.DRAW_BUFFER11_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.DRAW_BUFFER12_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.DRAW_BUFFER13_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.DRAW_BUFFER14_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.DRAW_BUFFER15_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.MAX_COLOR_ATTACHMENTS_WEBGL;\\n\\n/** @type {number} */\\nWEBGL_draw_buffers.prototype.MAX_DRAW_BUFFERS_WEBGL;\\n\\n/**\\n * @param {Array} buffers Draw buffers.\\n * @return {undefined}\\n */\\nWEBGL_draw_buffers.prototype.drawBuffersWEBGL = function(buffers) {};\\n\\n\\n/**\\n * @see http://www.khronos.org/registry/webgl/extensions/ANGLE_instanced_arrays/\\n * @constructor\\n */\\nfunction ANGLE_instanced_arrays() {}\\n\\n\\n/** @type {number} */\\nANGLE_instanced_arrays.prototype.VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE;\\n\\n\\n/**\\n * @param {number} mode Primitive type.\\n * @param {number} first First vertex.\\n * @param {number} count Number of vertices per instance.\\n * @param {number} primcount Number of instances.\\n * @return {undefined}\\n */\\nANGLE_instanced_arrays.prototype.drawArraysInstancedANGLE = function(\\n mode, first, count, primcount) {};\\n\\n\\n/**\\n * @param {number} mode Primitive type.\\n * @param {number} count Number of vertex indices per instance.\\n * @param {number} type Type of a vertex index.\\n * @param {number} offset Offset to the first vertex index.\\n * @param {number} primcount Number of instances.\\n * @return {undefined}\\n */\\nANGLE_instanced_arrays.prototype.drawElementsInstancedANGLE = function(\\n mode, count, type, offset, primcount) {};\\n\\n\\n/**\\n * @param {number} index Attribute index.\\n * @param {number} divisor Instance divisor.\\n * @return {undefined}\\n */\\nANGLE_instanced_arrays.prototype.vertexAttribDivisorANGLE = function(\\n index, divisor) {};\\n\\n","externs/webgl2.js":"/*\\n * Copyright 2018 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Definitions for WebGL functions as described at\\n * http://www.khronos.org/registry/webgl/specs/latest/\\n *\\n * This file is current up to the WebGL 2.0 spec.\\n *\\n * This relies on webgl.js and html5.js being included for WebGL1, Canvas and\\n * Typed Array support.\\n *\\n * @externs\\n */\\n\\n\\n/**\\n * @constructor\\n * @extends {WebGLRenderingContext}\\n */\\nfunction WebGL2RenderingContext() {}\\n\\n\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNPACK_ROW_LENGTH;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNPACK_SKIP_ROWS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNPACK_SKIP_PIXELS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.PACK_ROW_LENGTH;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.PACK_SKIP_ROWS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.PACK_SKIP_PIXELS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.COLOR;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DEPTH;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.STENCIL;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RED;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RGB8;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RGBA8;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RGB10_A2;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.TEXTURE_BINDING_3D;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNPACK_SKIP_IMAGES;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNPACK_IMAGE_HEIGHT;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.TEXTURE_3D;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.TEXTURE_WRAP_R;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_3D_TEXTURE_SIZE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNSIGNED_INT_2_10_10_10_REV;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_ELEMENTS_VERTICES;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_ELEMENTS_INDICES;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.TEXTURE_MIN_LOD;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.TEXTURE_MAX_LOD;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.TEXTURE_BASE_LEVEL;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.TEXTURE_MAX_LEVEL;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MIN;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DEPTH_COMPONENT24;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_TEXTURE_LOD_BIAS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.TEXTURE_COMPARE_MODE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.TEXTURE_COMPARE_FUNC;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.CURRENT_QUERY;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.QUERY_RESULT;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.QUERY_RESULT_AVAILABLE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.STREAM_READ;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.STREAM_COPY;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.STATIC_READ;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.STATIC_COPY;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DYNAMIC_READ;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DYNAMIC_COPY;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_DRAW_BUFFERS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DRAW_BUFFER0;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DRAW_BUFFER1;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DRAW_BUFFER2;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DRAW_BUFFER3;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DRAW_BUFFER4;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DRAW_BUFFER5;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DRAW_BUFFER6;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DRAW_BUFFER7;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DRAW_BUFFER8;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DRAW_BUFFER9;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DRAW_BUFFER10;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DRAW_BUFFER11;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DRAW_BUFFER12;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DRAW_BUFFER13;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DRAW_BUFFER14;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DRAW_BUFFER15;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_FRAGMENT_UNIFORM_COMPONENTS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_VERTEX_UNIFORM_COMPONENTS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.SAMPLER_3D;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.SAMPLER_2D_SHADOW;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.FRAGMENT_SHADER_DERIVATIVE_HINT;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.PIXEL_PACK_BUFFER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.PIXEL_UNPACK_BUFFER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.PIXEL_PACK_BUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.PIXEL_UNPACK_BUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.FLOAT_MAT2x3;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.FLOAT_MAT2x4;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.FLOAT_MAT3x2;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.FLOAT_MAT3x4;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.FLOAT_MAT4x2;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.FLOAT_MAT4x3;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.SRGB;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.SRGB8;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.SRGB8_ALPHA8;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.COMPARE_REF_TO_TEXTURE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RGBA32F;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RGB32F;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RGBA16F;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RGB16F;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.VERTEX_ATTRIB_ARRAY_INTEGER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_ARRAY_TEXTURE_LAYERS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MIN_PROGRAM_TEXEL_OFFSET;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_PROGRAM_TEXEL_OFFSET;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_VARYING_COMPONENTS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.TEXTURE_2D_ARRAY;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.TEXTURE_BINDING_2D_ARRAY;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.R11F_G11F_B10F;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNSIGNED_INT_10F_11F_11F_REV;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RGB9_E5;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNSIGNED_INT_5_9_9_9_REV;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.TRANSFORM_FEEDBACK_BUFFER_MODE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.TRANSFORM_FEEDBACK_VARYINGS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.TRANSFORM_FEEDBACK_BUFFER_START;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.TRANSFORM_FEEDBACK_BUFFER_SIZE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RASTERIZER_DISCARD;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS = 0x8C8A;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.INTERLEAVED_ATTRIBS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.SEPARATE_ATTRIBS;\\n\\n/** @type {number} */\\nWebGL2Renderi'; +a.a+="ngContext.TRANSFORM_FEEDBACK_BUFFER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.TRANSFORM_FEEDBACK_BUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RGBA32UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RGB32UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RGBA16UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RGB16UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RGBA8UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RGB8UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RGBA32I;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RGB32I;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RGBA16I;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RGB16I;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RGBA8I;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RGB8I;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RED_INTEGER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RGB_INTEGER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RGBA_INTEGER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.SAMPLER_2D_ARRAY;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.SAMPLER_2D_ARRAY_SHADOW;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.SAMPLER_CUBE_SHADOW;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNSIGNED_INT_VEC2;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNSIGNED_INT_VEC3;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNSIGNED_INT_VEC4;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.INT_SAMPLER_2D;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.INT_SAMPLER_3D;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.INT_SAMPLER_CUBE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.INT_SAMPLER_2D_ARRAY;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNSIGNED_INT_SAMPLER_2D;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNSIGNED_INT_SAMPLER_3D;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNSIGNED_INT_SAMPLER_CUBE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNSIGNED_INT_SAMPLER_2D_ARRAY;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DEPTH_COMPONENT32F;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DEPTH32F_STENCIL8;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.FLOAT_32_UNSIGNED_INT_24_8_REV;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.FRAMEBUFFER_ATTACHMENT_RED_SIZE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.FRAMEBUFFER_ATTACHMENT_GREEN_SIZE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.FRAMEBUFFER_ATTACHMENT_BLUE_SIZE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.FRAMEBUFFER_DEFAULT;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DEPTH_STENCIL_ATTACHMENT;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DEPTH_STENCIL;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNSIGNED_INT_24_8;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DEPTH24_STENCIL8;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNSIGNED_NORMALIZED;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DRAW_FRAMEBUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.READ_FRAMEBUFFER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.DRAW_FRAMEBUFFER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.READ_FRAMEBUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RENDERBUFFER_SAMPLES;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_COLOR_ATTACHMENTS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.COLOR_ATTACHMENT1;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.COLOR_ATTACHMENT2;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.COLOR_ATTACHMENT3;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.COLOR_ATTACHMENT4;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.COLOR_ATTACHMENT5;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.COLOR_ATTACHMENT6;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.COLOR_ATTACHMENT7;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.COLOR_ATTACHMENT8;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.COLOR_ATTACHMENT9;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.COLOR_ATTACHMENT10;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.COLOR_ATTACHMENT11;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.COLOR_ATTACHMENT12;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.COLOR_ATTACHMENT13;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.COLOR_ATTACHMENT14;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.COLOR_ATTACHMENT15;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.FRAMEBUFFER_INCOMPLETE_MULTISAMPLE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_SAMPLES;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.HALF_FLOAT;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RG;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RG_INTEGER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.R8;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RG8;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.R16F;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.R32F;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RG16F;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RG32F;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.R8I;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.R8UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.R16I;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.R16UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.R32I;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.R32UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RG8I;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RG8UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RG16I;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RG16UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RG32I;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RG32UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.VERTEX_ARRAY_BINDING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.R8_SNORM;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RG8_SNORM;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RGB8_SNORM;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RGBA8_SNORM;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.SIGNED_NORMALIZED;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.COPY_READ_BUFFER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.COPY_WRITE_BUFFER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.COPY_READ_BUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.COPY_WRITE_BUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNIFORM_BUFFER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNIFORM_BUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNIFORM_BUFFER_START;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNIFORM_BUFFER_SIZE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_VERTEX_UNIFORM_BLOCKS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_FRAGMENT_UNIFORM_BLOCKS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_COMBINED_UNIFORM_BLOCKS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_UNIFORM_BUFFER_BINDINGS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_UNIFORM_BLOCK_SIZE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNIFORM_BUFFER_OFFSET_ALIGNMENT;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.ACTIVE_UNIFORM_BLOCKS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNIFORM_TYPE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNIFORM_SIZE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNIFORM_BLOCK_INDEX;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNIFORM_OFFSET;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNIFORM_ARRAY_STRIDE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNIFORM_MATRIX_STRIDE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNIFORM_IS_ROW_MAJOR;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNIFORM_BLOCK_BINDING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNIFORM_BLOCK_DATA_SIZE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNIFORM_BLOCK_ACTIVE_UNIFORMS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.INVALID_INDEX;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_VERTEX_OUTPUT_COMPONENTS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_FRAGMENT_INPUT_COMPONENTS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_SERVER_WAIT_TIMEOUT;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.OBJECT_TYPE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.SYNC_CONDITION;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.SYNC_STATUS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.SYNC_FLAGS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.SYNC_FENCE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.SYNC_GPU_COMMANDS_COMPLETE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.UNSIGNALED;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.SIGNALED;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.ALREADY_SIGNALED;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.TIMEOUT_EXPIRED;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.CONDITION_SATISFIED;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.WAIT_FAILED;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.SYNC_FLUSH_COMMANDS_BIT;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.VERTEX_ATTRIB_ARRAY_DIVISOR;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.ANY_SAMPLES_PASSED;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.ANY_SAMPLES_PASSED_CONSERVATIVE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.SAMPLER_BINDING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.RGB10_A2UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.INT_2_10_10_10_REV;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.TRANSFORM_FEEDBACK;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.TRANSFORM_FEEDBACK_PAUSED;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.TRANSFORM_FEEDBACK_ACTIVE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.TRANSFORM_FEEDBACK_BINDING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.TEXTURE_IMMUTABLE_FORMAT;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_ELEMENT_INDEX;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.TEXTURE_IMMUTABLE_LEVELS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.TIMEOUT_IGNORED;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.MAX_CLIENT_WAIT_TIMEOUT_WEBGL;\\n\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNPACK_ROW_LENGTH;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNPACK_SKIP_ROWS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNPACK_SKIP_PIXELS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.PACK_ROW_LENGTH;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.PACK_SKIP_ROWS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.PACK_SKIP_PIXELS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.COLOR;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DEPTH;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.STENCIL;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RED;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RGB8;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RGBA8;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RGB10_A2;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TEXTURE_BINDING_3D;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNPACK_SKIP_IMAGES;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNPACK_IMAGE_HEIGHT;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TEXTURE_3D;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TEXTURE_WRAP_R;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_3D_TEXTURE_SIZE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNSIGNED_INT_2_10_10_10_REV;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_ELEMENTS_VERTICES;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_ELEMENTS_INDICES;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TEXTURE_MIN_LOD;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TEXTURE_MAX_LOD;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TEXTURE_BASE_LEVEL;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TEXTURE_MAX_LEVEL;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MIN;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DEPTH_COMPONENT24;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_TEXTURE_LOD_BIAS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TEXTURE_COMPARE_MODE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TEXTURE_COMPARE_FUNC;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.CURRENT_QUERY;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.QUERY_RESULT;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.QUERY_RESULT_AVAILABLE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.STREAM_READ;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.STREAM_COPY;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.STATIC_READ;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.STATIC_COPY;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DYNAMIC_READ;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DYNAMIC_COPY;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_DRAW_BUFFERS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DRAW_BUFFER0;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DRAW_BUFFER1;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DRAW_BUFFER2;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DRAW_BUFFER3;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DRAW_BUFFER4;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DRAW_BUFFER5;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DRAW_BUFFER6;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DRAW_BUFFER7;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DRAW_BUFFER8;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DRAW_BUFFER9;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DRAW_BUFFER10;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DRAW_BUFFER11;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DRAW_BUFFER12;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DRAW_BUFFER13;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DRAW_BUFFER14;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DRAW_BUFFER15;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_FRAGMENT_UNIFORM_COMPONENTS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_VERTEX_UNIFORM_COMPONENTS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.SAMPLER_3D;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.SAMPLER_2D_SHADOW;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.FRAGMENT_SHADER_DERIVATIVE_HINT;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.PIXEL_PACK_BUFFER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.PIXEL_UNPACK_BUFFER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.PIXEL_PACK_BUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.PIXEL_UNPACK_BUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.FLOAT_MAT2x3;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.FLOAT_MAT2x4;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.FLOAT_MAT3x2;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.FLOAT_MAT"; +a.a+="3x4;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.FLOAT_MAT4x2;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.FLOAT_MAT4x3;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.SRGB;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.SRGB8;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.SRGB8_ALPHA8;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.COMPARE_REF_TO_TEXTURE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RGBA32F;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RGB32F;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RGBA16F;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RGB16F;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.VERTEX_ATTRIB_ARRAY_INTEGER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_ARRAY_TEXTURE_LAYERS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MIN_PROGRAM_TEXEL_OFFSET;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_PROGRAM_TEXEL_OFFSET;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_VARYING_COMPONENTS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TEXTURE_2D_ARRAY;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TEXTURE_BINDING_2D_ARRAY;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.R11F_G11F_B10F;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNSIGNED_INT_10F_11F_11F_REV;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RGB9_E5;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNSIGNED_INT_5_9_9_9_REV;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TRANSFORM_FEEDBACK_BUFFER_MODE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TRANSFORM_FEEDBACK_VARYINGS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TRANSFORM_FEEDBACK_BUFFER_START;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TRANSFORM_FEEDBACK_BUFFER_SIZE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RASTERIZER_DISCARD;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS =\\n 0x8C8A;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.INTERLEAVED_ATTRIBS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.SEPARATE_ATTRIBS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TRANSFORM_FEEDBACK_BUFFER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TRANSFORM_FEEDBACK_BUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RGBA32UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RGB32UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RGBA16UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RGB16UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RGBA8UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RGB8UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RGBA32I;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RGB32I;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RGBA16I;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RGB16I;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RGBA8I;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RGB8I;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RED_INTEGER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RGB_INTEGER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RGBA_INTEGER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.SAMPLER_2D_ARRAY;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.SAMPLER_2D_ARRAY_SHADOW;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.SAMPLER_CUBE_SHADOW;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNSIGNED_INT_VEC2;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNSIGNED_INT_VEC3;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNSIGNED_INT_VEC4;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.INT_SAMPLER_2D;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.INT_SAMPLER_3D;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.INT_SAMPLER_CUBE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.INT_SAMPLER_2D_ARRAY;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNSIGNED_INT_SAMPLER_2D;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNSIGNED_INT_SAMPLER_3D;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNSIGNED_INT_SAMPLER_CUBE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNSIGNED_INT_SAMPLER_2D_ARRAY;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DEPTH_COMPONENT32F;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DEPTH32F_STENCIL8;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.FLOAT_32_UNSIGNED_INT_24_8_REV;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.FRAMEBUFFER_ATTACHMENT_RED_SIZE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.FRAMEBUFFER_ATTACHMENT_GREEN_SIZE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.FRAMEBUFFER_ATTACHMENT_BLUE_SIZE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.FRAMEBUFFER_DEFAULT;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DEPTH_STENCIL_ATTACHMENT;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DEPTH_STENCIL;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNSIGNED_INT_24_8;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DEPTH24_STENCIL8;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNSIGNED_NORMALIZED;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DRAW_FRAMEBUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.READ_FRAMEBUFFER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.DRAW_FRAMEBUFFER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.READ_FRAMEBUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RENDERBUFFER_SAMPLES;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_COLOR_ATTACHMENTS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.COLOR_ATTACHMENT1;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.COLOR_ATTACHMENT2;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.COLOR_ATTACHMENT3;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.COLOR_ATTACHMENT4;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.COLOR_ATTACHMENT5;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.COLOR_ATTACHMENT6;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.COLOR_ATTACHMENT7;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.COLOR_ATTACHMENT8;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.COLOR_ATTACHMENT9;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.COLOR_ATTACHMENT10;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.COLOR_ATTACHMENT11;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.COLOR_ATTACHMENT12;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.COLOR_ATTACHMENT13;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.COLOR_ATTACHMENT14;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.COLOR_ATTACHMENT15;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.FRAMEBUFFER_INCOMPLETE_MULTISAMPLE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_SAMPLES;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.HALF_FLOAT;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RG;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RG_INTEGER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.R8;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RG8;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.R16F;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.R32F;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RG16F;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RG32F;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.R8I;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.R8UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.R16I;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.R16UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.R32I;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.R32UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RG8I;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RG8UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RG16I;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RG16UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RG32I;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RG32UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.VERTEX_ARRAY_BINDING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.R8_SNORM;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RG8_SNORM;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RGB8_SNORM;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RGBA8_SNORM;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.SIGNED_NORMALIZED;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.COPY_READ_BUFFER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.COPY_WRITE_BUFFER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.COPY_READ_BUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.COPY_WRITE_BUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNIFORM_BUFFER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNIFORM_BUFFER_BINDING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNIFORM_BUFFER_START;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNIFORM_BUFFER_SIZE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_VERTEX_UNIFORM_BLOCKS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_FRAGMENT_UNIFORM_BLOCKS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_COMBINED_UNIFORM_BLOCKS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_UNIFORM_BUFFER_BINDINGS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_UNIFORM_BLOCK_SIZE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNIFORM_BUFFER_OFFSET_ALIGNMENT;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.ACTIVE_UNIFORM_BLOCKS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNIFORM_TYPE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNIFORM_SIZE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNIFORM_BLOCK_INDEX;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNIFORM_OFFSET;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNIFORM_ARRAY_STRIDE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNIFORM_MATRIX_STRIDE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNIFORM_IS_ROW_MAJOR;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNIFORM_BLOCK_BINDING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNIFORM_BLOCK_DATA_SIZE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNIFORM_BLOCK_ACTIVE_UNIFORMS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.INVALID_INDEX;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_VERTEX_OUTPUT_COMPONENTS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_FRAGMENT_INPUT_COMPONENTS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_SERVER_WAIT_TIMEOUT;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.OBJECT_TYPE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.SYNC_CONDITION;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.SYNC_STATUS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.SYNC_FLAGS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.SYNC_FENCE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.SYNC_GPU_COMMANDS_COMPLETE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.UNSIGNALED;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.SIGNALED;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.ALREADY_SIGNALED;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TIMEOUT_EXPIRED;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.CONDITION_SATISFIED;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.WAIT_FAILED;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.SYNC_FLUSH_COMMANDS_BIT;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.VERTEX_ATTRIB_ARRAY_DIVISOR;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.ANY_SAMPLES_PASSED;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.ANY_SAMPLES_PASSED_CONSERVATIVE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.SAMPLER_BINDING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.RGB10_A2UI;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.INT_2_10_10_10_REV;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TRANSFORM_FEEDBACK;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TRANSFORM_FEEDBACK_PAUSED;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TRANSFORM_FEEDBACK_ACTIVE;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TRANSFORM_FEEDBACK_BINDING;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TEXTURE_IMMUTABLE_FORMAT;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_ELEMENT_INDEX;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TEXTURE_IMMUTABLE_LEVELS;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.TIMEOUT_IGNORED;\\n\\n/** @type {number} */\\nWebGL2RenderingContext.prototype.MAX_CLIENT_WAIT_TIMEOUT_WEBGL;\\n\\n/* Buffer objects */\\n\\n/**\\n * @param {number} target\\n * @param {?ArrayBufferView|?ArrayBuffer|number} data\\n * @param {number} usage\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_length\\n * @return {undefined}\\n * @override\\n */\\nWebGL2RenderingContext.prototype.bufferData = function(\\n target, data, usage, opt_srcOffset, opt_length) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} offset\\n * @param {?ArrayBufferView|?ArrayBuffer} data\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_length\\n * @return {undefined}\\n * @override\\n */\\nWebGL2RenderingContext.prototype.bufferSubData = function(\\n target, offset, data, opt_srcOffset, opt_length) {};\\n\\n/**\\n * @param {number} readTarget\\n * @param {number} writeTarget\\n * @param {number} readOffset\\n * @param {number} writeOffset\\n * @param {number} size\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.copyBufferSubData = function(\\n readTarget, writeTarget, readOffset, writeOffset, size) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} srcByte"; +a.a+="Offset\\n * @param {?ArrayBufferView|?ArrayBuffer} dstBuffer\\n * @param {number=} opt_dstOffset\\n * @param {number=} opt_length\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.getBufferSubData = function(\\n target, srcByteOffset, dstBuffer, opt_dstOffset, opt_length) {};\\n\\n/* Framebuffer objects */\\n\\n/**\\n * @param {number} srcX0\\n * @param {number} srcY0\\n * @param {number} srcX1\\n * @param {number} srcY1\\n * @param {number} dstX0\\n * @param {number} dstY0\\n * @param {number} dstX1\\n * @param {number} dstY1\\n * @param {number} mask\\n * @param {number} filter\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.blitFramebuffer = function(\\n srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} attachment\\n * @param {?WebGLTexture} texture\\n * @param {number} level\\n * @param {number} layer\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.framebufferTextureLayer = function(\\n target, attachment, texture, level, layer) {};\\n\\n/**\\n * @param {number} target\\n * @param {!Array} attachments\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.invalidateFramebuffer = function(\\n target, attachments) {};\\n\\n/**\\n * @param {number} target\\n * @param {!Array} attachments\\n * @param {number} x\\n * @param {number} y\\n * @param {number} width\\n * @param {number} height\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.invalidateSubFramebuffer = function(\\n target, attachments, x, y, width, height) {};\\n\\n/**\\n * @param {number} src\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.readBuffer = function(src) {};\\n\\n/* Renderbuffer objects */\\n\\n/**\\n * @param {number} target\\n * @param {number} internalformat\\n * @param {number} pname\\n * @return {*}\\n * @nosideeffects\\n */\\nWebGL2RenderingContext.prototype.getInternalformatParameter = function(\\n target, internalformat, pname) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} samples\\n * @param {number} internalformat\\n * @param {number} width\\n * @param {number} height\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.renderbufferStorageMultisample = function(\\n target, samples, internalformat, width, height) {};\\n\\n/* Texture objects */\\n\\n/**\\n * @param {number} target\\n * @param {number} levels\\n * @param {number} internalformat\\n * @param {number} width\\n * @param {number} height\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.texStorage2D = function(\\n target, levels, internalformat, width, height) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} levels\\n * @param {number} internalformat\\n * @param {number} width\\n * @param {number} height\\n * @param {number} depth\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.texStorage3D = function(\\n target, levels, internalformat, width, height, depth) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} level\\n * @param {number} internalformat\\n * @param {number} formatOrWidth\\n * @param {number} typeOrHeight\\n * @param {?TexImageSource|number} imgOrBorder\\n * @param {number=} opt_format\\n * @param {number=} opt_type\\n * @param {?ArrayBufferView|?TexImageSource|number=} opt_imgOrOffset\\n * @param {number=} opt_srcOffset\\n * @return {undefined}\\n * @override\\n */\\nWebGL2RenderingContext.prototype.texImage2D = function(\\n target, level, internalformat, formatOrWidth, typeOrHeight, imgOrBorder,\\n opt_format, opt_type, opt_imgOrOffset, opt_srcOffset) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} level\\n * @param {number} xoffset\\n * @param {number} yoffset\\n * @param {number} formatOrWidth\\n * @param {number} typeOrHeight\\n * @param {?TexImageSource|number} dataOrFormat\\n * @param {number=} opt_type\\n * @param {?ArrayBufferView|?TexImageSource|number=} opt_imgOrOffset\\n * @param {number=} opt_srcOffset\\n * @return {undefined}\\n * @override\\n */\\nWebGL2RenderingContext.prototype.texSubImage2D = function(\\n target, level, xoffset, yoffset, formatOrWidth, typeOrHeight, dataOrFormat,\\n opt_type, opt_imgOrOffset, opt_srcOffset) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} level\\n * @param {number} internalformat\\n * @param {number} width\\n * @param {number} height\\n * @param {number} depth\\n * @param {number} border\\n * @param {number} format\\n * @param {number} type\\n * @param {?ArrayBufferView|?TexImageSource|number} srcData\\n * @param {number=} opt_srcOffset\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.texImage3D = function(\\n target, level, internalformat, width, height, depth, border, format, type,\\n srcData, opt_srcOffset) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} level\\n * @param {number} xoffset\\n * @param {number} yoffset\\n * @param {number} zoffset\\n * @param {number} width\\n * @param {number} height\\n * @param {number} depth\\n * @param {number} format\\n * @param {number} type\\n * @param {?ArrayBufferView|?TexImageSource|number} srcData\\n * @param {number=} opt_srcOffset\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.texSubImage3D = function(\\n target, level, xoffset, yoffset, zoffset, width, height, depth, format,\\n type, srcData, opt_srcOffset) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} level\\n * @param {number} xoffset\\n * @param {number} yoffset\\n * @param {number} zoffset\\n * @param {number} x\\n * @param {number} y\\n * @param {number} width\\n * @param {number} height\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.copyTexSubImage3D = function(\\n target, level, xoffset, yoffset, zoffset, x, y, width, height) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} level\\n * @param {number} internalformat\\n * @param {number} width\\n * @param {number} height\\n * @param {number} border\\n * @param {?ArrayBufferView|number} srcDataOrSize\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_srcLengthOverride\\n * @return {undefined}\\n * @override\\n */\\nWebGL2RenderingContext.prototype.compressedTexImage2D = function(\\n target, level, internalformat, width, height, border, srcDataOrSize,\\n opt_srcOffset, opt_srcLengthOverride) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} level\\n * @param {number} internalformat\\n * @param {number} width\\n * @param {number} height\\n * @param {number} depth\\n * @param {number} border\\n * @param {!ArrayBufferView|number} srcDataOrSize\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_srcLengthOverride\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.compressedTexImage3D = function(\\n target, level, internalformat, width, height, depth, border, srcDataOrSize,\\n opt_srcOffset, opt_srcLengthOverride) {};\\n\\n\\n/**\\n * @param {number} target\\n * @param {number} level\\n * @param {number} xoffset\\n * @param {number} yoffset\\n * @param {number} width\\n * @param {number} height\\n * @param {number} format\\n * @param {?ArrayBufferView|number} srcDataOrSize\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_srcLengthOverride\\n * @return {undefined}\\n * @override\\n */\\nWebGL2RenderingContext.prototype.compressedTexSubImage2D = function(\\n target, level, xoffset, yoffset, width, height, format, srcDataOrSize,\\n opt_srcOffset, opt_srcLengthOverride) {};\\n\\n\\n/**\\n * @param {number} target\\n * @param {number} level\\n * @param {number} xoffset\\n * @param {number} yoffset\\n * @param {number} zoffset\\n * @param {number} width\\n * @param {number} height\\n * @param {number} depth\\n * @param {number} format\\n * @param {!ArrayBufferView|number} srcDataOrSize\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_srcLengthOverride\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.compressedTexSubImage3D = function(\\n target, level, xoffset, yoffset, zoffset, width, height, depth, format,\\n srcDataOrSize, opt_srcOffset, opt_srcLengthOverride) {};\\n\\n/* Programs and shaders */\\n\\n/**\\n * @param {!WebGLProgram} program\\n * @param {string} name\\n * @return {number}\\n * @nosideeffects\\n */\\nWebGL2RenderingContext.prototype.getFragDataLocation = function(\\n program, name) {};\\n\\n/* Uniforms */\\n\\n/**\\n * @param {?WebGLUniformLocation} location\\n * @param {number} v0\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.uniform1ui = function(location, v0) {};\\n\\n/**\\n * @param {?WebGLUniformLocation} location\\n * @param {number} v0\\n * @param {number} v1\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.uniform2ui = function(location, v0, v1) {};\\n\\n/**\\n * @param {?WebGLUniformLocation} location\\n * @param {number} v0\\n * @param {number} v1\\n * @param {number} v2\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.uniform3ui = function(location, v0, v1, v2) {};\\n\\n/**\\n * @param {?WebGLUniformLocation} location\\n * @param {number} v0\\n * @param {number} v1\\n * @param {number} v2\\n * @param {number} v3\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.uniform4ui = function(\\n location, v0, v1, v2, v3) {};\\n\\n\\n/**\\n * @param {?WebGLUniformLocation} location\\n * @param {?Float32Array|?Array} data\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_srcLength\\n * @return {undefined}\\n * @override\\n */\\nWebGL2RenderingContext.prototype.uniform1fv = function(\\n location, data, opt_srcOffset, opt_srcLength) {};\\n\\n/**\\n * @param {?WebGLUniformLocation} location\\n * @param {?Float32Array|?Array} data\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_srcLength\\n * @return {undefined}\\n * @override\\n */\\nWebGL2RenderingContext.prototype.uniform2fv = function(\\n location, data, opt_srcOffset, opt_srcLength) {};\\n\\n/**\\n * @param {?WebGLUniformLocation} location\\n * @param {?Float32Array|?Array} data\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_srcLength\\n * @return {undefined}\\n * @override\\n */\\nWebGL2RenderingContext.prototype.uniform3fv = function(\\n location, data, opt_srcOffset, opt_srcLength) {};\\n\\n/**\\n * @param {?WebGLUniformLocation} location\\n * @param {?Float32Array|?Array} data\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_srcLength\\n * @return {undefined}\\n * @override\\n */\\nWebGL2RenderingContext.prototype.uniform4fv = function(\\n location, data, opt_srcOffset, opt_srcLength) {};\\n\\n/**\\n * @param {?WebGLUniformLocation} location\\n * @param {?Int32Array|?Array|?Array} data\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_srcLength\\n * @return {undefined}\\n * @override\\n */\\nWebGL2RenderingContext.prototype.uniform1iv = function(\\n location, data, opt_srcOffset, opt_srcLength) {};\\n\\n/**\\n * @param {?WebGLUniformLocation} location\\n * @param {?Int32Array|?Array|?Array} data\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_srcLength\\n * @return {undefined}\\n * @override\\n */\\nWebGL2RenderingContext.prototype.uniform2iv = function(\\n location, data, opt_srcOffset, opt_srcLength) {};\\n\\n/**\\n * @param {?WebGLUniformLocation} location\\n * @param {?Int32Array|?Array|?Array} data\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_srcLength\\n * @return {undefined}\\n * @override\\n */\\nWebGL2RenderingContext.prototype.uniform3iv = function(\\n location, data, opt_srcOffset, opt_srcLength) {};\\n\\n/**\\n * @param {?WebGLUniformLocation} location\\n * @param {?Int32Array|?Array|?Array} data\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_srcLength\\n * @return {undefined}\\n * @override\\n */\\nWebGL2RenderingContext.prototype.uniform4iv = function(\\n location, data, opt_srcOffset, opt_srcLength) {};\\n\\n\\n/**\\n * @param {?WebGLUniformLocation} location\\n * @param {!Uint32Array|!Array|!Array} data\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_srcLength\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.uniform1uiv = function(\\n location, data, opt_srcOffset, opt_srcLength) {};\\n\\n/**\\n * @param {?WebGLUniformLocation} location\\n * @param {!Uint32Array|!Array|!Array} data\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_srcLength\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.uniform2uiv = function(\\n location, data, opt_srcOffset, opt_srcLength) {};\\n\\n/**\\n * @param {?WebGLUniformLocation} location\\n * @param {!Uint32Array|!Array|!Array} data\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_srcLength\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.uniform3uiv = function(\\n location, data, opt_srcOffset, opt_srcLength) {};\\n\\n/**\\n * @param {?WebGLUniformLocation} location\\n * @param {!Uint32Array|!Array|!Array} data\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_srcLength\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.uniform4uiv = function(\\n location, data, opt_srcOffset, opt_srcLength) {};\\n\\n/**\\n * @param {?WebGLUniformLocation} location\\n * @param {boolean} transpose\\n * @param {?Float32Array|?Array} data\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_srcLength\\n * @return {undefined}\\n * @override\\n */\\nWebGL2RenderingContext.prototype.uniformMatrix2fv = function(\\n location, transpose, data, opt_srcOffset, opt_srcLength) {};\\n\\n/**\\n * @param {?WebGLUniformLocation} location\\n * @param {boolean} transpose\\n * @param {!Float32Array|!Array} data\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_srcLength\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.uniformMatrix3x2fv = function(\\n location, transpose, data, opt_srcOffset, opt_srcLength) {};\\n\\n/**\\n * @param {?WebGLUniformLocation} location\\n * @param {boolean} transpose\\n * @param {!Float32Array|!Array} data\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_srcLength\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.uniformMatrix4x2fv = function(\\n location, transpose, data, opt_srcOffset, opt_srcLength) {};\\n\\n/**\\n * @param {?WebGLUniformLocation} location\\n * @param {boolean} transpose\\n * @param {!Float32Array|!Array} data\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_srcLength\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.uniformMatrix2x3fv = function(\\n location, transpose, data, opt_srcOffset, opt_srcLength) {};\\n\\n/**\\n * @param {?WebGLUniformLocation} location\\n * @param {boolean} transpose\\n * @param {?Float32Array|?Array} data\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_srcLength\\n * @return {undefined}\\n * @override\\n */\\nWebGL2RenderingContext.prototype.uniformMatrix3fv = function(\\n location, transpose, data, opt_srcOffset, opt_srcLength) {};\\n\\n/**\\n * @param {?WebGLUniformLocation} location\\n * @param {boolean} transpose\\n * @param {!Float32Array|!Array} data\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_srcLength\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.uniformMatrix4x3fv = function(\\n location, transpose, data, opt_srcOffset, opt_srcLength) {};\\n\\n/**\\n * @param {?WebGLUniformLocation} location\\n * @param {boolean} transpose\\n * @param {!Float32Array|!Array} data\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_srcLength\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.uniformMatrix2x4fv = function(\\n location, transpose, data, opt_srcOffset, opt_srcLength) {};\\n\\n/**\\n * @param {?WebGLUniformLocation} location\\n * @param {boolean} transpose\\n * @param {!Float32Array|!Array} data\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_srcLength\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.uniformMatrix3x4fv = function(\\n location, transpose, data, opt_srcOffset, opt_srcLength) {};\\n\\n/**\\n * @param {?WebGLUniformLocation} location\\n * @param {boolean} transpose\\n * @param {?Float32Array|?Array} data\\n * @param {number=} opt_srcOffset\\n * @param {number=} opt_srcLength\\n * @return {undefined}\\n * @override\\n */\\nWebGL2RenderingContext.prototype.uniformMatrix4fv = function(\\n location, transpose, data, opt_srcOffset, opt_srcLength) {};\\n\\n/* Vertex attribs */\\n\\n/**\\n * @param {number} index\\n * @param {number} x\\n * @"; +a.a+="param {number} y\\n * @param {number} z\\n * @param {number} w\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.vertexAttribI4i = function(\\n index, x, y, z, w) {};\\n\\n/**\\n * @param {number} index\\n * @param {!Int32Array|!Array|!Array} values\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.vertexAttribI4iv = function(index, values) {};\\n\\n/**\\n * @param {number} index\\n * @param {number} x\\n * @param {number} y\\n * @param {number} z\\n * @param {number} w\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.vertexAttribI4ui = function(\\n index, x, y, z, w) {};\\n\\n/**\\n * @param {number} index\\n * @param {!Uint32Array|!Array|!Array} values\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.vertexAttribI4uiv = function(index, values) {};\\n\\n/**\\n * @param {number} index\\n * @param {number} size\\n * @param {number} type\\n * @param {number} stride\\n * @param {number} offset\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.vertexAttribIPointer = function(\\n index, size, type, stride, offset) {};\\n\\n/* Writing to the drawing buffer */\\n\\n/**\\n * @param {number} index\\n * @param {number} divisor\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.vertexAttribDivisor = function(\\n index, divisor) {};\\n\\n/**\\n * @param {number} mode\\n * @param {number} first\\n * @param {number} count\\n * @param {number} instanceCount\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.drawArraysInstanced = function(\\n mode, first, count, instanceCount) {};\\n\\n/**\\n * @param {number} mode\\n * @param {number} count\\n * @param {number} type\\n * @param {number} offset\\n * @param {number} instanceCount\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.drawElementsInstanced = function(\\n mode, count, type, offset, instanceCount) {};\\n\\n/**\\n * @param {number} mode\\n * @param {number} start\\n * @param {number} end\\n * @param {number} count\\n * @param {number} type\\n * @param {number} offset\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.drawRangeElements = function(\\n mode, start, end, count, type, offset) {};\\n\\n/* Reading back pixels */\\n\\n/**\\n * @param {number} x\\n * @param {number} y\\n * @param {number} width\\n * @param {number} height\\n * @param {number} format\\n * @param {number} type\\n * @param {?ArrayBufferView|number} dstDataOrOffset\\n * @param {number=} opt_dstOffset\\n * @return {undefined}\\n * @override\\n */\\nWebGL2RenderingContext.prototype.readPixels = function(\\n x, y, width, height, format, type, dstDataOrOffset, opt_dstOffset) {};\\n\\n/* Multiple Render Targets */\\n\\n/**\\n * @param {!Array} buffers\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.drawBuffers = function(buffers) {};\\n\\n\\n/**\\n * @param {number} buffer\\n * @param {number} drawbuffer\\n * @param {!Float32Array|!Array} values\\n * @param {number=} opt_srcOffset\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.clearBufferfv = function(\\n buffer, drawbuffer, values, opt_srcOffset) {};\\n\\n/**\\n * @param {number} buffer\\n * @param {number} drawbuffer\\n * @param {!Int32Array|!Array|!Array} values\\n * @param {number=} opt_srcOffset\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.clearBufferiv = function(\\n buffer, drawbuffer, values, opt_srcOffset) {};\\n\\n/**\\n * @param {number} buffer\\n * @param {number} drawbuffer\\n * @param {!Uint32Array|!Array|!Array} values\\n * @param {number=} opt_srcOffset\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.clearBufferuiv = function(\\n buffer, drawbuffer, values, opt_srcOffset) {};\\n\\n/**\\n * @param {number} buffer\\n * @param {number} drawbuffer\\n * @param {number} depth\\n * @param {number} stencil\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.clearBufferfi = function(\\n buffer, drawbuffer, depth, stencil) {};\\n\\n/* Query Objects */\\n\\n/**\\n * @return {?WebGLQuery}\\n */\\nWebGL2RenderingContext.prototype.createQuery = function() {};\\n\\n/**\\n * @param {?WebGLQuery} query\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.deleteQuery = function(query) {};\\n\\n/**\\n * @param {?WebGLQuery} query\\n * @return {boolean}\\n */\\nWebGL2RenderingContext.prototype.isQuery = function(query) {};\\n\\n/**\\n * @param {number} target\\n * @param {!WebGLQuery} query\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.beginQuery = function(target, query) {};\\n\\n/**\\n * @param {number} target\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.endQuery = function(target) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} pname\\n * @return {?WebGLQuery}\\n * @nosideeffects\\n */\\nWebGL2RenderingContext.prototype.getQuery = function(target, pname) {};\\n\\n/**\\n * @param {!WebGLQuery} query\\n * @param {number} pname\\n * @return {*}\\n */\\nWebGL2RenderingContext.prototype.getQueryParameter = function(query, pname) {};\\n\\n/* Sampler Objects */\\n\\n/**\\n * @return {?WebGLSampler}\\n */\\nWebGL2RenderingContext.prototype.createSampler = function() {};\\n\\n/**\\n * @param {?WebGLSampler} sampler\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.deleteSampler = function(sampler) {};\\n\\n/**\\n * @param {?WebGLSampler} sampler\\n * @return {boolean}\\n */\\nWebGL2RenderingContext.prototype.isSampler = function(sampler) {};\\n\\n/**\\n * @param {number} unit\\n * @param {?WebGLSampler} sampler\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.bindSampler = function(unit, sampler) {};\\n\\n/**\\n * @param {!WebGLSampler} sampler\\n * @param {number} pname\\n * @param {number} param\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.samplerParameteri = function(\\n sampler, pname, param) {};\\n\\n/**\\n * @param {!WebGLSampler} sampler\\n * @param {number} pname\\n * @param {number} param\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.samplerParameterf = function(\\n sampler, pname, param) {};\\n\\n/**\\n * @param {!WebGLSampler} sampler\\n * @param {number} pname\\n * @return {*}\\n * @nosideeffects\\n */\\nWebGL2RenderingContext.prototype.getSamplerParameter = function(\\n sampler, pname) {};\\n\\n/* Sync objects */\\n\\n/**\\n * @param {number} condition\\n * @param {number} flags\\n * @return {?WebGLSync}\\n */\\nWebGL2RenderingContext.prototype.fenceSync = function(condition, flags) {};\\n\\n/**\\n * @param {?WebGLSync} sync\\n * @return {boolean}\\n */\\nWebGL2RenderingContext.prototype.isSync = function(sync) {};\\n\\n/**\\n * @param {?WebGLSync} sync\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.deleteSync = function(sync) {};\\n\\n/**\\n * @param {!WebGLSync} sync\\n * @param {number} flags\\n * @param {number} timeout\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.clientWaitSync = function(\\n sync, flags, timeout) {};\\n\\n/**\\n * @param {!WebGLSync} sync\\n * @param {number} flags\\n * @param {number} timeout\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.waitSync = function(sync, flags, timeout) {};\\n\\n/**\\n * @param {!WebGLSync} sync\\n * @param {number} pname\\n * @return {*}\\n */\\nWebGL2RenderingContext.prototype.getSyncParameter = function(sync, pname) {};\\n\\n/* Transform Feedback */\\n\\n/**\\n * @return {?WebGLTransformFeedback}\\n */\\nWebGL2RenderingContext.prototype.createTransformFeedback = function() {};\\n\\n/**\\n * @param {?WebGLTransformFeedback} tf\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.deleteTransformFeedback = function(tf) {};\\n\\n/**\\n * @param {?WebGLTransformFeedback} tf\\n * @return {boolean}\\n */\\nWebGL2RenderingContext.prototype.isTransformFeedback = function(tf) {};\\n\\n/**\\n * @param {number} target\\n * @param {?WebGLTransformFeedback} tf\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.bindTransformFeedback = function(\\n target, tf) {};\\n\\n/**\\n * @param {number} primitiveMode\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.beginTransformFeedback = function(\\n primitiveMode) {};\\n\\n/**\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.endTransformFeedback = function() {};\\n\\n/**\\n * @param {!WebGLProgram} program\\n * @param {!Array} varyings\\n * @param {number} bufferMode\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.transformFeedbackVaryings = function(\\n program, varyings, bufferMode) {};\\n\\n/**\\n * @param {!WebGLProgram} program\\n * @param {number} index\\n * @return {?WebGLActiveInfo}\\n * @nosideeffects\\n */\\nWebGL2RenderingContext.prototype.getTransformFeedbackVarying = function(\\n program, index) {};\\n\\n/**\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.pauseTransformFeedback = function() {};\\n\\n/**\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.resumeTransformFeedback = function() {};\\n\\n/* Uniform Buffer Objects and Transform Feedback Buffers */\\n\\n/**\\n * @param {number} target\\n * @param {number} index\\n * @param {?WebGLBuffer} buffer\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.bindBufferBase = function(\\n target, index, buffer) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} index\\n * @param {?WebGLBuffer} buffer\\n * @param {number} offset\\n * @param {number} size\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.bindBufferRange = function(\\n target, index, buffer, offset, size) {};\\n\\n/**\\n * @param {number} target\\n * @param {number} index\\n * @return {*}\\n */\\nWebGL2RenderingContext.prototype.getIndexedParameter = function(\\n target, index) {};\\n\\n/**\\n * @param {!WebGLProgram} program\\n * @param {!Array} uniformNames\\n * @return {!Array}\\n */\\nWebGL2RenderingContext.prototype.getUniformIndices = function(\\n program, uniformNames) {};\\n\\n/**\\n * @param {!WebGLProgram} program\\n * @param {!Array} uniformIndices\\n * @param {number} pname\\n * @return {*}\\n */\\nWebGL2RenderingContext.prototype.getActiveUniforms = function(\\n program, uniformIndices, pname) {};\\n\\n/**\\n * @param {!WebGLProgram} program\\n * @param {string} uniformBlockName\\n * @return {number}\\n * @nosideeffects\\n */\\nWebGL2RenderingContext.prototype.getUniformBlockIndex = function(\\n program, uniformBlockName) {};\\n\\n/**\\n * @param {!WebGLProgram} program\\n * @param {number} uniformBlockIndex\\n * @param {number} pname\\n * @return {*}\\n */\\nWebGL2RenderingContext.prototype.getActiveUniformBlockParameter = function(\\n program, uniformBlockIndex, pname) {};\\n\\n/**\\n * @param {!WebGLProgram} program\\n * @param {number} uniformBlockIndex\\n * @return {?string}\\n * @nosideeffects\\n */\\nWebGL2RenderingContext.prototype.getActiveUniformBlockName = function(\\n program, uniformBlockIndex) {};\\n\\n/**\\n * @param {!WebGLProgram} program\\n * @param {number} uniformBlockIndex\\n * @param {number} uniformBlockBinding\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.uniformBlockBinding = function(\\n program, uniformBlockIndex, uniformBlockBinding) {};\\n\\n/* Vertex Array Objects */\\n\\n/**\\n * @return {?WebGLVertexArrayObject}\\n */\\nWebGL2RenderingContext.prototype.createVertexArray = function() {};\\n\\n/**\\n * @param {?WebGLVertexArrayObject} vertexArray\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.deleteVertexArray = function(vertexArray) {};\\n\\n/**\\n * @param {?WebGLVertexArrayObject} vertexArray\\n * @return {boolean}\\n */\\nWebGL2RenderingContext.prototype.isVertexArray = function(vertexArray) {};\\n\\n/**\\n * @param {?WebGLVertexArrayObject} array\\n * @return {undefined}\\n */\\nWebGL2RenderingContext.prototype.bindVertexArray = function(array) {};\\n\\n\\n/**\\n * @constructor\\n * @extends {WebGLObject}\\n */\\nfunction WebGLQuery() {}\\n\\n\\n/**\\n * @constructor\\n * @extends {WebGLObject}\\n */\\nfunction WebGLSampler() {}\\n\\n\\n/**\\n * @constructor\\n * @extends {WebGLObject}\\n */\\nfunction WebGLSync() {}\\n\\n\\n/**\\n * @constructor\\n * @extends {WebGLObject}\\n */\\nfunction WebGLTransformFeedback() {}\\n\\n\\n/**\\n * @constructor\\n * @extends {WebGLObject}\\n */\\nfunction WebGLVertexArrayObject() {}\\n\",\"externs/fetchapi.js\":\"/*\\n * Copyright 2014 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n/**\\n * @fileoverview Definitions of the fetch api.\\n *\\n * This api is still in development and not yet stable. Use at your\\n * own risk.\\n *\\n * Based on Living Standard \u2014 Last Updated 17 August 2016\\n *\\n * @see https://fetch.spec.whatwg.org/\\n * @externs\\n */\\n\\n\\n/**\\n * @typedef {string}\\n * @see https://w3c.github.io/webappsec-referrer-policy/#enumdef-referrerpolicy\\n * Possible values: '', 'no-referrer', 'no-referrer-when-downgrade',\\n * 'same-origin', 'origin', 'strict-origin', 'origin-when-cross-origin',\\n * 'strict-origin-when-cross-origin', 'unsafe-url'\\n */\\nvar ReferrerPolicy;\\n\\n\\n/**\\n * @typedef {!Headers|!Array>|!Object}\\n * @see https://fetch.spec.whatwg.org/#headersinit\\n */\\nvar HeadersInit;\\n\\n\\n/**\\n * @param {!HeadersInit=} opt_headersInit\\n * @constructor\\n * @implements {Iterable>}\\n * @see https://fetch.spec.whatwg.org/#headers\\n */\\nfunction Headers(opt_headersInit) {}\\n\\n/**\\n * @param {string} name\\n * @param {string} value\\n * @return {undefined}\\n */\\nHeaders.prototype.append = function(name, value) {};\\n\\n/**\\n * @param {string} name\\n * @return {undefined}\\n */\\nHeaders.prototype.delete = function(name) {};\\n\\n/** @return {!IteratorIterable>} */\\nHeaders.prototype.entries = function() {};\\n\\n/**\\n * @param {string} name\\n * @return {?string}\\n */\\nHeaders.prototype.get = function(name) {};\\n\\n/**\\n * @param {string} name\\n * @return {!Array}\\n */\\nHeaders.prototype.getAll = function(name) {};\\n\\n/**\\n * @param {string} name\\n * @return {boolean}\\n */\\nHeaders.prototype.has = function(name) {};\\n\\n/** @return {!IteratorIterable} */\\nHeaders.prototype.keys = function() {};\\n\\n/**\\n * @param {string} name\\n * @param {string} value\\n * @return {undefined}\\n */\\nHeaders.prototype.set = function(name, value) {};\\n\\n/** @return {!Iterator} */\\nHeaders.prototype.values = function() {};\\n\\n/** @return {!Iterator>} */\\nHeaders.prototype[Symbol.iterator] = function() {};\\n\\n\\n/**\\n * @typedef {!Blob|!BufferSource|!FormData|string}\\n * @see https://fetch.spec.whatwg.org/#bodyinit\\n */\\nvar BodyInit;\\n\\n\\n/**\\n * @typedef {!BodyInit|!ReadableStream}\\n * @see https://fetch.spec.whatwg.org/#responsebodyinit\\n */\\nvar ResponseBodyInit;\\n\\n\\n/**\\n * @interface\\n * @see https://fetch.spec.whatwg.org/#body\\n */\\nfunction Body() {};\\n\\n/** @type {boolean} */\\nBody.prototype.bodyUsed;\\n\\n/** @return {!Promise} */\\nBody.prototype.arrayBuffer = function() {};\\n\\n/** @return {!Promise} */\\nBody.prototype.blob = function() {};\\n\\n/** @return {!Promise} */\\nBody.prototype.formData = function() {};\\n\\n/** @return {!Promise<*>} */\\nBody.prototype.json = function() {};\\n\\n/** @return {!Promise} */\\nBody.prototype.text = function() {};\\n\\n\\n/**\\n * @typedef {!Request|string}\\n * @see https://fetch.spec.whatwg.org/#requestinfo\\n */\\nvar RequestInfo;\\n\\n\\n/**\\n * @param {!RequestInfo} input\\n * @param {!RequestInit=} opt_init\\n * @constructor\\n * @implements {Body}\\n * @see https://fetch.spec.whatwg.org/#request\\n */\\nfunction Request(input, opt_init) {}\\n\\n/** @override */\\nRequest.prototype.bodyUsed;\\n\\n/** @override */\\nRequest.prototype.arrayBuffer = function() {};\\n\\n/** @override */\\nRequest.prototype.blob = function() {};\\n\\n/** @override */\\nRequest.prototype.formData = function() {};\\n\\n/** @override */\\nRequest.prototype.json = function() {};\\n\\n/** @override */\\nRequest.prototype.text = function() {};\\n\\n/** @type {string} */\\nRequest.prototype.method;\\n\\n/** @type {string} */\\nRequest.prototype.url;\\n\\n/** @type {!Headers} */\\nRe"; +a.a+="quest.prototype.headers;\\n\\n/** @type {!FetchRequestType} */\\nRequest.prototype.type;\\n\\n/** @type {!RequestDestination} */\\nRequest.prototype.destination;\\n\\n/** @type {string} */\\nRequest.prototype.referrer;\\n\\n/** @type {!RequestMode} */\\nRequest.prototype.mode;\\n\\n/** @type {!RequestCredentials} */\\nRequest.prototype.credentials;\\n\\n/** @type {!RequestCache} */\\nRequest.prototype.cache;\\n\\n/** @type {!RequestRedirect} */\\nRequest.prototype.redirect;\\n\\n/** @type {string} */\\nRequest.prototype.integrity;\\n\\n/** @type {boolean} */\\nRequest.prototype.isHistoryNavigation;\\n\\n/** @type {(undefined|boolean)} */\\nRequest.prototype.keepalive;\\n\\n/** @return {!Request} */\\nRequest.prototype.clone = function() {};\\n\\n\\n/**\\n * @record\\n * @see https://fetch.spec.whatwg.org/#requestinit\\n */\\nfunction RequestInit() {};\\n\\n/** @type {(undefined|string)} */\\nRequestInit.prototype.method;\\n\\n/** @type {(undefined|!HeadersInit)} */\\nRequestInit.prototype.headers;\\n\\n/** @type {(undefined|?BodyInit)} */\\nRequestInit.prototype.body;\\n\\n/** @type {(undefined|string)} */\\nRequestInit.prototype.referrer;\\n\\n/** @type {(undefined|!ReferrerPolicy)} */\\nRequestInit.prototype.referrerPolicy;\\n\\n/** @type {(undefined|!RequestMode)} */\\nRequestInit.prototype.mode;\\n\\n/** @type {(undefined|!RequestCredentials)} */\\nRequestInit.prototype.credentials;\\n\\n/** @type {(undefined|!RequestCache)} */\\nRequestInit.prototype.cache;\\n\\n/** @type {(undefined|!RequestRedirect)} */\\nRequestInit.prototype.redirect;\\n\\n/** @type {(undefined|string)} */\\nRequestInit.prototype.integrity;\\n\\n/** @type {(undefined|!AbortSignal)} */\\nRequestInit.prototype.signal;\\n\\n/** @type {(undefined|boolean)} */\\nRequestInit.prototype.keepalive;\\n\\n/** @type {(undefined|null)} */\\nRequestInit.prototype.window;\\n\\n/**\\n * @typedef {string}\\n * @see https://fetch.spec.whatwg.org/#requesttype\\n * Possible values: '', 'audio', 'font', 'image', 'script', 'style',\\n * 'track', 'video'\\n */\\nvar FetchRequestType;\\n\\n\\n/**\\n * @typedef {string}\\n * @see https://fetch.spec.whatwg.org/#requestdestination\\n * Possible values: '', 'document', 'embed', 'font', 'image', 'manifest',\\n * 'media', 'object', 'report', 'script', 'serviceworker', 'sharedworker',\\n * 'style', 'worker', 'xslt'\\n */\\nvar RequestDestination;\\n\\n\\n/**\\n * @typedef {string}\\n * @see https://fetch.spec.whatwg.org/#requestmode\\n * Possible values: 'navigate', 'same-origin', 'no-cors', 'cors'\\n */\\nvar RequestMode ;\\n\\n\\n/**\\n * @typedef {string}\\n * @see https://fetch.spec.whatwg.org/#requestcredentials\\n * Possible values: 'omit', 'same-origin', 'include'\\n */\\nvar RequestCredentials;\\n\\n\\n/**\\n * @typedef {string}\\n * @see https://fetch.spec.whatwg.org/#requestcache\\n * Possible values: 'default', 'no-store', 'reload', 'no-cache', 'force-cache',\\n * 'only-if-cached'\\n */\\nvar RequestCache;\\n\\n\\n/**\\n * @typedef {string}\\n * @see https://fetch.spec.whatwg.org/#requestredirect\\n * Possible values: 'follow', 'error', 'manual'\\n */\\nvar RequestRedirect;\\n\\n\\n/**\\n * @param {?ResponseBodyInit=} opt_body\\n * @param {!ResponseInit=} opt_init\\n * @constructor\\n * @implements {Body}\\n * @see https://fetch.spec.whatwg.org/#response\\n */\\nfunction Response(opt_body, opt_init) {}\\n\\n/** @return {!Response} */\\nResponse.error = function() {};\\n\\n/**\\n * @param {string} url\\n * @param {number=} opt_status\\n * @return {!Response}\\n */\\nResponse.redirect = function(url, opt_status) {};\\n\\n/** @override */\\nResponse.prototype.bodyUsed;\\n\\n/** @override */\\nResponse.prototype.arrayBuffer = function() {};\\n\\n/** @override */\\nResponse.prototype.blob = function() {};\\n\\n/** @override */\\nResponse.prototype.formData = function() {};\\n\\n/** @override */\\nResponse.prototype.json = function() {};\\n\\n/** @override */\\nResponse.prototype.text = function() {};\\n\\n/** @type {!ResponseType} */\\nResponse.prototype.type;\\n\\n/** @type {string} */\\nResponse.prototype.url;\\n\\n/** @type {boolean} */\\nResponse.prototype.redirected;\\n\\n/** @type {number} */\\nResponse.prototype.status;\\n\\n/** @type {boolean} */\\nResponse.prototype.ok;\\n\\n/** @type {string} */\\nResponse.prototype.statusText;\\n\\n/** @type {!Headers} */\\nResponse.prototype.headers;\\n\\n/** @type {?ReadableStream} */\\nResponse.prototype.body;\\n\\n/** @type {!Promise} */\\nResponse.prototype.trailer;\\n\\n/** @return {!Response} */\\nResponse.prototype.clone = function() {};\\n\\n\\n/**\\n * @record\\n * @see https://fetch.spec.whatwg.org/#responseinit\\n */\\nfunction ResponseInit() {};\\n\\n/** @type {(undefined|number)} */\\nResponseInit.prototype.status;\\n\\n/** @type {(undefined|string)} */\\nResponseInit.prototype.statusText;\\n\\n/** @type {(undefined|!HeadersInit)} */\\nResponseInit.prototype.headers;\\n\\n\\n/**\\n * @typedef {string}\\n * @see https://fetch.spec.whatwg.org/#responsetype\\n * Possible values: 'basic', 'cors', 'default', 'error', 'opaque',\\n * 'opaqueredirect'\\n */\\nvar ResponseType;\\n\\n/**\\n * @param {!RequestInfo} input\\n * @param {!RequestInit=} opt_init\\n * @return {!Promise}\\n * @see https://fetch.spec.whatwg.org/#fetch-method\\n */\\nfunction fetch(input, opt_init) {}\\n\\n/**\\n * @param {!RequestInfo} input\\n * @param {!RequestInit=} opt_init\\n * @return {!Promise}\\n * @see https://fetch.spec.whatwg.org/#fetch-method\\n */\\nWindow.prototype.fetch = function(input, opt_init) {};\\n\\n/**\\n * @param {!RequestInfo} input\\n * @param {!RequestInit=} opt_init\\n * @return {!Promise}\\n * @see https://fetch.spec.whatwg.org/#fetch-method\\n */\\nWorkerGlobalScope.prototype.fetch = function(input, opt_init) {};\\n\\n/**\\n * if WorkerOptions.type = 'module', it specifies how `scriptURL` is fetched.\\n * WorkerOptions is defined in html5.js.\\n * @type {!RequestCredentials|undefined}\\n */\\nWorkerOptions.prototype.credentials;\\n\",\"externs/streamsapi.js\":\"/*\\n * Copyright 2015 The Closure Compiler Authors\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n/**\\n * @fileoverview Streams API definitions\\n *\\n * Based on Living Standard \u2014 Last Updated 5 August 2016\\n * https://streams.spec.whatwg.org/commit-snapshots/34ecaadbcce8df9943d7a2cdb7fca4dc25914df4/\\n *\\n * @see https://streams.spec.whatwg.org/\\n * @externs\\n */\\n\\n\\n/** @typedef {{ value:*, done:boolean }} */\\nvar IteratorResult;\\n\\n\\n/**\\n * @typedef {!CountQueuingStrategy|!ByteLengthQueuingStrategy|{\\n * size: (undefined|function(*): number),\\n * highWaterMark: number\\n * }}\\n */\\nvar QueuingStrategy;\\n\\n/**\\n * The TransformStreamDefaultController class has methods that allow\\n * manipulation of the associated ReadableStream and WritableStream.\\n *\\n * This class cannot be directly constructed and is instead passed by the\\n * TransformStream to the methods of its transformer.\\n *\\n * @interface\\n * @see https://streams.spec.whatwg.org/#ts-default-controller-class\\n */\\nfunction TransformStreamDefaultController() {};\\n\\n/**\\n * @type {number}\\n * @see https://streams.spec.whatwg.org/#ts-default-controller-desired-size\\n */\\nTransformStreamDefaultController.prototype.desiredSize;\\n\\n/**\\n * @param {*} chunk\\n * @return {undefined}\\n * @see https://streams.spec.whatwg.org/#ts-default-controller-enqueue\\n */\\nTransformStreamDefaultController.prototype.enqueue = function(chunk) {};\\n\\n/**\\n * @param {*} reason\\n * @return {undefined}\\n * @see https://streams.spec.whatwg.org/#ts-default-controller-error\\n */\\nTransformStreamDefaultController.prototype.error = function(reason) {};\\n\\n/**\\n * @return {undefined}\\n * @see https://streams.spec.whatwg.org/#ts-default-controller-terminate\\n */\\nTransformStreamDefaultController.prototype.terminate = function() {};\\n\\n\\n/**\\n * @record\\n * @see https://streams.spec.whatwg.org/#transformer-api\\n */\\nfunction TransformStreamTransformer() {};\\n\\n/**\\n * @type {(undefined|\\n * function(!TransformStreamDefaultController):(!IThenable<*>|undefined))}\\n */\\nTransformStreamTransformer.prototype.start;\\n\\n/**\\n * @type {(undefined|\\n * function(*,\\n * !TransformStreamDefaultController):(!IThenable<*>|undefined))}\\n */\\nTransformStreamTransformer.prototype.transform;\\n\\n/**\\n * @type {(undefined|\\n * function(!TransformStreamDefaultController):(!IThenable<*>|undefined))}\\n */\\nTransformStreamTransformer.prototype.flush;\\n\\n\\n/**\\n * @record\\n */\\nfunction TransformStream() {};\\n\\n/** @type {!WritableStream} */\\nTransformStream.prototype.writable;\\n\\n/** @type {!ReadableStream} */\\nTransformStream.prototype.readable;\\n\\n\\n/**\\n * @record\\n */\\nfunction PipeOptions() {};\\n\\n/** @type {undefined|boolean} */\\nPipeOptions.prototype.preventClose;\\n\\n/** @type {undefined|boolean} */\\nPipeOptions.prototype.preventAbort;\\n\\n/** @type {undefined|boolean} */\\nPipeOptions.prototype.preventCancel;\\n\\n\\n/**\\n * @record\\n */\\nfunction ReadableStreamSource() {};\\n\\n/**\\n * @type {(undefined|\\n * function((!ReadableByteStreamController|!ReadableStreamDefaultController)):(!IThenable<*>|undefined))}\\n */\\nReadableStreamSource.prototype.start;\\n\\n/**\\n * @type {(undefined|\\n * function((!ReadableByteStreamController|!ReadableStreamDefaultController)):(!IThenable<*>|undefined))}\\n */\\nReadableStreamSource.prototype.pull;\\n\\n/** @type {(undefined|function(*):(!Promise<*>|undefined))} */\\nReadableStreamSource.prototype.cancel;\\n\\n/** @type {(undefined|string)} */\\nReadableStreamSource.prototype.type;\\n\\n/** @type {(undefined|number)} */\\nReadableStreamSource.prototype.autoAllocateChunkSize;\\n\\n\\n/**\\n * @param {!ReadableStreamSource=} opt_underlyingSource\\n * @param {!QueuingStrategy=} opt_queuingStrategy\\n * @constructor\\n * @see https://streams.spec.whatwg.org/#rs-class\\n */\\nfunction ReadableStream(opt_underlyingSource, opt_queuingStrategy) {};\\n\\n/**\\n * @type {boolean}\\n * @see https://streams.spec.whatwg.org/#rs-locked\\n */\\nReadableStream.prototype.locked;\\n\\n/**\\n * @param {*} reason\\n * @return {!Promise}\\n * @see https://streams.spec.whatwg.org/#rs-cancel\\n */\\nReadableStream.prototype.cancel = function(reason) {};\\n\\n/**\\n * @param {{ mode:(undefined|string) }=} opt_options\\n * @return {(!ReadableStreamDefaultReader|!ReadableStreamBYOBReader)}\\n * @see https://streams.spec.whatwg.org/#rs-get-reader\\n */\\nReadableStream.prototype.getReader = function(opt_options) {};\\n\\n/**\\n * @param {!TransformStream} transform\\n * @param {!PipeOptions=} opt_options\\n * @return {!ReadableStream}\\n * @see https://streams.spec.whatwg.org/#rs-pipe-through\\n */\\nReadableStream.prototype.pipeThrough = function(transform, opt_options) {};\\n\\n/**\\n * @param {!WritableStream} dest\\n * @param {!PipeOptions=} opt_options\\n * @return {!Promise}\\n * @see https://streams.spec.whatwg.org/#rs-pipe-to\\n */\\nReadableStream.prototype.pipeTo = function(dest, opt_options) {};\\n\\n/**\\n * @return {!Array}\\n * @see https://streams.spec.whatwg.org/#rs-tee\\n */\\nReadableStream.prototype.tee = function() {};\\n\\n\\n/**\\n * The ReadableStreamDefaultReader constructor is generally not meant to be used directly;\\n * instead, a stream\u2019s getReader() method should be used.\\n *\\n * @interface\\n * @see https://streams.spec.whatwg.org/#default-reader-class\\n */\\nfunction ReadableStreamDefaultReader() {};\\n\\n/**\\n * @type {!Promise}\\n * @see https://streams.spec.whatwg.org/#default-reader-closed\\n */\\nReadableStreamDefaultReader.prototype.closed;\\n\\n/**\\n * @param {*} reason\\n * @return {!Promise<*>}\\n * @see https://streams.spec.whatwg.org/#default-reader-cancel\\n */\\nReadableStreamDefaultReader.prototype.cancel = function(reason) {};\\n\\n/**\\n * @return {!Promise}\\n * @see https://streams.spec.whatwg.org/#default-reader-read\\n */\\nReadableStreamDefaultReader.prototype.read = function() {};\\n\\n/**\\n * @return {undefined}\\n * @see https://streams.spec.whatwg.org/#default-reader-release-lock\\n */\\nReadableStreamDefaultReader.prototype.releaseLock = function() {};\\n\\n\\n/**\\n * The ReadableStreamBYOBReader constructor is generally not meant to be used\\n * directly; instead, a stream\u2019s getReader() method should be used.\\n *\\n * @interface\\n * @see https://streams.spec.whatwg.org/#byob-reader-class\\n */\\nfunction ReadableStreamBYOBReader() {};\\n\\n/**\\n * @type {!Promise}\\n * @see https://streams.spec.whatwg.org/#byob-reader-closed\\n */\\nReadableStreamBYOBReader.prototype.closed;\\n\\n/**\\n * @param {*} reason\\n * @return {!Promise<*>}\\n * @see https://streams.spec.whatwg.org/#byob-reader-cancel\\n */\\nReadableStreamBYOBReader.prototype.cancel = function(reason) {};\\n\\n/**\\n * @param {!ArrayBufferView} view\\n * @return {!Promise}\\n * @see https://streams.spec.whatwg.org/#byob-reader-read\\n */\\nReadableStreamBYOBReader.prototype.read = function(view) {};\\n\\n/**\\n * @return {undefined}\\n * @see https://streams.spec.whatwg.org/#byob-reader-release-lock\\n */\\nReadableStreamBYOBReader.prototype.releaseLock = function() {};\\n\\n\\n/**\\n * The ReadableStreamDefaultController constructor cannot be used directly;\\n * it only works on a ReadableStream that is in the middle of being constructed.\\n *\\n * @interface\\n * @see https://streams.spec.whatwg.org/#rs-default-controller-class\\n */\\nfunction ReadableStreamDefaultController() {};\\n\\n/**\\n * @type {number}\\n * @see https://streams.spec.whatwg.org/#rs-default-controller-desired-size\\n */\\nReadableStreamDefaultController.prototype.desiredSize;\\n\\n/**\\n * @return {undefined}\\n * @see https://streams.spec.whatwg.org/#rs-default-controller-close\\n */\\nReadableStreamDefaultController.prototype.close = function() {};\\n\\n/**\\n * @param {*} chunk\\n * @return {undefined}\\n * @see https://streams.spec.whatwg.org/#rs-default-controller-enqueue\\n */\\nReadableStreamDefaultController.prototype.enqueue = function(chunk) {};\\n\\n/**\\n * @param {*} err\\n * @return {undefined}\\n * @see https://streams.spec.whatwg.org/#rs-default-controller-error\\n */\\nReadableStreamDefaultController.prototype.error = function(err) {};\\n\\n\\n/**\\n * The ReadableByteStreamController constructor cannot be used directly;\\n * it only works on a ReadableStream that is in the middle of being constructed.\\n *\\n * @interface\\n * @see https://streams.spec.whatwg.org/#rbs-controller-class\\n */\\nfunction ReadableByteStreamController() {};\\n\\n/**\\n * @type {!ReadableStreamBYOBRequest}\\n * @see https://streams.spec.whatwg.org/#rbs-controller-byob-request\\n */\\nReadableByteStreamController.prototype.byobRequest;\\n\\n/**\\n * @type {number}\\n * @see https://streams.spec.whatwg.org/#rbs-controller-desired-size\\n */\\nReadableByteStreamController.prototype.desiredSize;\\n\\n/**\\n * @return {undefined}\\n * @see https://streams.spec.whatwg.org/#rbs-controller-close\\n */\\nReadableByteStreamController.prototype.close = function() {};\\n\\n/**\\n * @param {!ArrayBufferView} chunk\\n * @return {undefined}\\n * @see https://streams.spec.whatwg.org/#rbs-controller-enqueue\\n */\\nReadableByteStreamController.prototype.enqueue = function(chunk) {};\\n\\n/**\\n * @param {*} err\\n * @return {undefined}\\n * @see https://streams.spec.whatwg.org/#rbs-controller-error\\n */\\nReadableByteStreamController.prototype.error = function(err) {};\\n\\n\\n/**\\n * @interface\\n * @see https://streams.spec.whatwg.org/#rs-byob-request-class\\n */\\nfunction ReadableStreamBYOBRequest() {};\\n\\n/**\\n * @type {!ArrayBufferView}\\n * @see https://streams.spec.whatwg.org/#rs-byob-request-view\\n */\\nReadableStreamBYOBRequest.prototype.view;\\n\\n/**\\n * @param {number} bytesWritten\\n * @return {undefined}\\n * @see https://streams.spec.whatwg.org/#rs-byob-request-respond\\n */\\nReadableStreamBYOBRequest.prototype.respond = function(bytesWritten) {};\\n\\n/**\\n * @param {!ArrayBufferView} view\\n * @return {undefined}\\n * @see https://streams.spec.whatwg.org/#rs-byob-request-respond-with-new-view\\n */\\nReadableStreamBYOBRequest.prototype.respondWithNewView = function(view) {};\\n\\n\\n/**\\n * @record\\n */\\nfunction WritableStreamSink() {};\\n\\n/** @type {(undefined|"; +a.a+="function(!WritableStreamDefaultController):(!IThenable<*>|undefined))}*/\\nWritableStreamSink.prototype.start;\\n\\n/** @type {(undefined|function(*, !WritableStreamDefaultController):(!IThenable<*>|undefined))}*/\\nWritableStreamSink.prototype.write;\\n\\n/** @type {(undefined|function():(!IThenable<*>|undefined))} */\\nWritableStreamSink.prototype.close;\\n\\n/** @type {(undefined|function(*):(!IThenable<*>|undefined))} */\\nWritableStreamSink.prototype.abort;\\n\\n\\n/**\\n * @param {!WritableStreamSink=} opt_underlyingSink\\n * @param {!QueuingStrategy=} opt_queuingStrategy\\n * @constructor\\n * @see https://streams.spec.whatwg.org/#ws-class\\n */\\nfunction WritableStream(opt_underlyingSink, opt_queuingStrategy) {};\\n\\n/**\\n * @type {boolean}\\n * @see https://streams.spec.whatwg.org/#ws-locked\\n */\\nWritableStream.prototype.locked;\\n\\n/**\\n * @param {*} reason\\n * @return {!Promise}\\n * @see https://streams.spec.whatwg.org/#ws-abort\\n */\\nWritableStream.prototype.abort = function(reason) {};\\n\\n/**\\n * @return {!WritableStreamDefaultWriter}\\n * @see https://streams.spec.whatwg.org/#ws-get-writer\\n */\\nWritableStream.prototype.getWriter = function() {};\\n\\n\\n/**\\n * @interface\\n * @see https://streams.spec.whatwg.org/#default-writer-class\\n */\\nfunction WritableStreamDefaultWriter() {};\\n\\n/**\\n * @type {!Promise}\\n * @see https://streams.spec.whatwg.org/#default-writer-closed\\n */\\nWritableStreamDefaultWriter.prototype.closed;\\n\\n/**\\n * @type {number}\\n * @see https://streams.spec.whatwg.org/#default-writer-desiredSize\\n */\\nWritableStreamDefaultWriter.prototype.desiredSize;\\n\\n/**\\n * @type {!Promise}\\n * @see https://streams.spec.whatwg.org/#default-writer-ready\\n */\\nWritableStreamDefaultWriter.prototype.ready;\\n\\n/**\\n * @param {*} reason\\n * @return {!Promise}\\n * @see https://streams.spec.whatwg.org/#default-writer-abort\\n */\\nWritableStreamDefaultWriter.prototype.abort = function(reason) {};\\n\\n/**\\n * @return {!Promise}\\n * @see https://streams.spec.whatwg.org/#default-writer-close\\n */\\nWritableStreamDefaultWriter.prototype.close = function() {};\\n\\n/**\\n * @return {undefined}\\n * @see https://streams.spec.whatwg.org/#default-writer-release-lock\\n */\\nWritableStreamDefaultWriter.prototype.releaseLock = function() {};\\n\\n/**\\n * @param {*} chunk\\n * @return {!Promise}\\n * @see https://streams.spec.whatwg.org/#default-writer-write\\n */\\nWritableStreamDefaultWriter.prototype.write = function(chunk) {};\\n\\n\\n/**\\n * The WritableStreamDefaultController constructor cannot be used directly;\\n * it only works on a WritableStream that is in the middle of being constructed.\\n *\\n * @interface\\n * @see https://streams.spec.whatwg.org/#ws-default-controller-class\\n */\\nfunction WritableStreamDefaultController() {};\\n\\n/**\\n * @param {*} err\\n * @return {!Promise}\\n * @see https://streams.spec.whatwg.org/#ws-default-controller-error\\n */\\nWritableStreamDefaultController.prototype.error = function(err) {};\\n\\n\\n/**\\n * @param {{ highWaterMark:number }} config\\n * @constructor\\n * @see https://streams.spec.whatwg.org/#blqs-class\\n */\\nfunction ByteLengthQueuingStrategy(config) {}\\n\\n/**\\n * If we don't want to be strict we can define chunk as {*}\\n * and return as {number|undefined}\\n *\\n * @param {{ byteLength:number }} chunk\\n * @return {number}\\n * @see https://streams.spec.whatwg.org/#blqs-size\\n */\\nByteLengthQueuingStrategy.prototype.size = function(chunk) {};\\n\\n\\n/**\\n * @param {{ highWaterMark:number }} config\\n * @constructor\\n * @see https://streams.spec.whatwg.org/#cqs-class\\n */\\nfunction CountQueuingStrategy(config) {}\\n\\n/**\\n * @param {*} chunk\\n * @return {number}\\n * @see https://streams.spec.whatwg.org/#cqs-size\\n */\\nCountQueuingStrategy.prototype.size = function(chunk) {};\\n\",\"js/base.js\":\"/*\\n * Copyright 2012 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n\\n/**\\n * @fileoverview The base namespace for code injected by the compiler\\n * at compile-time.\\n *\\n * @author nicksantos@google.com (Nick Santos)\\n */\\n\\n/** @const */\\nvar $jscomp = $jscomp || {};\\n\\n/** @const Locals for goog.scope */\\n$jscomp.scope = {};\\n\",\"js/es6/array.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Brings in all ES6 Array polyfills.\\n * @suppress {uselessCode}\\n */\\n'require es6/array/copywithin';\\n'require es6/array/entries';\\n'require es6/array/fill';\\n'require es6/array/find';\\n'require es6/array/findindex';\\n'require es6/array/flat';\\n'require es6/array/flatmap';\\n'require es6/array/from';\\n'require es6/array/includes';\\n'require es6/array/keys';\\n'require es6/array/of';\\n'require es6/array/values';\\n\",\"js/es6/array/copywithin.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Array.prototype.copyWithin', function(orig) {\\n // requires strict mode to throw for invalid `this` or params\\n 'use strict';\\n\\n if (orig) return orig;\\n\\n /**\\n * Copies elements from one part of the array to another.\\n *\\n * @this {!IArrayLike}\\n * @param {number} target Start index to copy elements to.\\n * @param {number} start Start index to copy elements from.\\n * @param {number=} opt_end Index from which to end copying.\\n * @return {!IArrayLike} The array, with the copy performed in-place.\\n * @template VALUE\\n */\\n var polyfill = function(target, start, opt_end) {\\n var len = this.length;\\n target = toInteger(target);\\n start = toInteger(start);\\n var end = opt_end === undefined ? len : toInteger(opt_end);\\n var to = target < 0 ? Math.max(len + target, 0) : Math.min(target, len);\\n var from = start < 0 ? Math.max(len + start, 0) : Math.min(start, len);\\n var final = end < 0 ? Math.max(len + end, 0) : Math.min(end, len);\\n if (to < from) {\\n while (from < final) {\\n if (from in this) {\\n this[to++] = this[from++];\\n } else {\\n delete this[to++];\\n from++;\\n }\\n }\\n } else {\\n final = Math.min(final, len + from - to);\\n to += final - from;\\n while (final > from) {\\n if (--final in this) {\\n this[--to] = this[final];\\n } else {\\n delete this[--to];\\n }\\n }\\n }\\n return this;\\n };\\n\\n /**\\n * @param {number} arg\\n * @return {number}\\n */\\n function toInteger(arg) {\\n var n = Number(arg);\\n if (n === Infinity || n === -Infinity) {\\n return n;\\n }\\n return n | 0;\\n }\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/array/entries.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n'require es6/util/iteratorfromarray';\\n\\n$jscomp.polyfill('Array.prototype.entries', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Returns an iterator of [key, value] arrays, one for each entry\\n * in the given array.\\n *\\n * @this {!IArrayLike}\\n * @return {!IteratorIterable>}\\n * @template VALUE\\n * @suppress {reportUnknownTypes}\\n */\\n var polyfill = function() {\\n return $jscomp.iteratorFromArray(\\n this, function(i, v) { return [i, v]; });\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/array/fill.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Array.prototype.fill', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Fills elements of an array with a constant value.\\n *\\n * @this {!IArrayLike}\\n * @param {VALUE} value Value to fill.\\n * @param {number=} opt_start Start index, or zero if omitted.\\n * @param {number=} opt_end End index, or length if omitted.\\n * @return {!IArrayLike} The array, with the fill performed in-place.\\n * @template VALUE\\n * @suppress {reportUnknownTypes, strictPrimitiveOperators}\\n */\\n var polyfill = function(value, opt_start, opt_end) {\\n var length = this.length || 0;\\n if (opt_start < 0) {\\n opt_start = Math.max(0, length + /** @type {number} */ (opt_start));\\n }\\n if (opt_end == null || opt_end > length) opt_end = length;\\n opt_end = Number(opt_end);\\n if (opt_end < 0) opt_end = Math.max(0, length + opt_end);\\n for (var i = Number(opt_start || 0); i < opt_end; i++) {\\n this[i] = value;\\n }\\n return this;\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/array/find.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/findinternal';\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Array.prototype.find', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Finds and returns an element that satisfies the given predicate.\\n *\\n * @this {!IArrayLike}\\n * @param {function(this: THIS, VALUE, number, !IArrayLike): *}\\n * callback\\n * @param {THIS=} opt_thisArg\\n * @return {VALUE|undefined} The found value, or undefined.\\n * @template VALUE, THIS\\n * @suppress {reportUnknownTypes}\\n */\\n var polyfill = function(callback, opt_thisArg) {\\n return $jscomp.findInternal(this, callback, opt_thisArg).v;\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/array/findindex.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/findinternal';\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Array.prototype.findIndex', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Finds an element that satisfies the given predicate, returning its index.\\n *\\n * @this {!IArrayLike}\\n * @param {function(this: THIS, VALUE, number, !IArrayLike): *}\\n * callback\\n * @param {THIS=} opt_thisArg\\n * @return {number} The found value, or undefined.\\n * @template VALUE, THIS\\n * @suppress {reportUnknownTypes}\\n */\\n var polyfill = function(callback, opt_thisArg) {\\n return $jscomp.findInternal(this, callback, opt_thisArg).i;\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/array/flat.js\":\"/*\\n * Copyright 2018 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/** @fileoverview @suppress {uselessCode} */\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Array.prototype.flat', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Polyfills Array.prototype.flat.\\n *\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat\\n *\\n * @param {*=} depth\\n * @return {!Array}\\n * @this {!IArrayLike}\\n * @template T, S\\n * @suppress {reportUnknownTypes}\\n */\\n var flat = function(depth) {\\n // TODO(sdh): Consider respecting Symbol.species (b/121061255).\\n depth = depth === undefined ? 1 : Number(depth);\\n var flattened = [];\\n for (var i = 0; i < this.length; i++) {\\n var element = this[i];\\n if (Array.isArray(element) && depth > 0) {\\n var inner = Array.prototype.flat.call(element, depth - 1);\\n flattened.push.apply(flattened, inner);\\n } else {\\n flattened.push(element);\\n }\\n }\\n return flattened;\\n };\\n\\n return flat;\\n}, 'es9', 'es5');\\n\",\"js/es6/array/flatmap.js\":\"/*\\n * Copyright 2018 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/** @fileoverview @suppress {uselessCode} */\\n'require util/p"; +a.a+="olyfill';\\n\\n$jscomp.polyfill('Array.prototype.flatMap', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Polyfills Array.prototype.flatMap.\\n *\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap\\n *\\n * @param {function(this: THIS, T, number, !IArrayLike): S|!Array} callback\\n * @param {THIS=} thisArg\\n * @return {!Array}\\n * @this {!IArrayLike}\\n * @template T, THIS, S\\n * @suppress {reportUnknownTypes}\\n */\\n var flatMap = function(callback, thisArg) {\\n var mapped = [];\\n for (var i = 0; i < this.length; i++) {\\n var result = callback.call(thisArg, this[i], i, this);\\n if (Array.isArray(result)) {\\n mapped.push.apply(mapped, result);\\n } else {\\n mapped.push(result);\\n }\\n }\\n return mapped;\\n };\\n\\n return flatMap;\\n}, 'es9', 'es5');\\n\",\"js/es6/array/from.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Array.from', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Creates a new Array from an array-like or iterable object.\\n *\\n *

      Polyfills the static function Array.from(). Does not support\\n * constructor inheritance (i.e. (subclass of Array).from), and\\n * relies on the compiler to check the validity of inputs rather\\n * than producing spec-compliant TypeErrors.\\n *\\n * @param {!IArrayLike|!Iterable} arrayLike\\n * An array-like or iterable.\\n * @param {(function(this: THIS, INPUT, number): OUTPUT)=} opt_mapFn\\n * Function to call on each argument.\\n * @param {THIS=} opt_thisArg\\n * Object to use as 'this' when calling mapFn.\\n * @return {!Array}\\n * @template INPUT, OUTPUT, THIS\\n * @suppress {reportUnknownTypes}\\n */\\n var polyfill = function(arrayLike, opt_mapFn, opt_thisArg) {\\n opt_mapFn = opt_mapFn != null ? opt_mapFn : function(x) { return x; };\\n var result = [];\\n // NOTE: this is cast to ? because [] on @struct is an error\\n var iteratorFunction = typeof Symbol != 'undefined' && Symbol.iterator &&\\n (/** @type {?} */ (arrayLike)[Symbol.iterator]);\\n if (typeof iteratorFunction == 'function') {\\n arrayLike = iteratorFunction.call(arrayLike);\\n var next;\\n var k = 0;\\n while (!(next = arrayLike.next()).done) {\\n result.push(\\n opt_mapFn.call(/** @type {?} */ (opt_thisArg), next.value, k++));\\n }\\n } else {\\n var len = arrayLike.length; // need to support non-iterables\\n for (var i = 0; i < len; i++) {\\n result.push(\\n opt_mapFn.call(/** @type {?} */ (opt_thisArg), arrayLike[i], i));\\n }\\n }\\n return result;\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/array/includes.js\":\"/*\\n * Copyright 2017 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require es6/object/is';\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Array.prototype.includes', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Polyfills Array.prototype.includes.\\n *\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes\\n *\\n * @this {!IArrayLike}\\n * @param {T} searchElement\\n * @param {number=} opt_fromIndex\\n * @return {boolean}\\n * @template T\\n * @suppress {reportUnknownTypes}\\n */\\n var includes = function(searchElement, opt_fromIndex) {\\n var array = this;\\n if (array instanceof String) {\\n array = /** @type {!IArrayLike} */ (String(array));\\n }\\n var len = array.length;\\n var i = opt_fromIndex || 0;\\n if (i < 0) {\\n i = Math.max(i + len, 0);\\n }\\n for (; i < len; i++) {\\n var element = array[i];\\n if (element === searchElement || Object.is(element, searchElement)) {\\n return true;\\n }\\n }\\n return false;\\n };\\n\\n return includes;\\n}, 'es7', 'es3');\\n\",\"js/es6/array/keys.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require es6/util/iteratorfromarray';\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Array.prototype.keys', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Returns an iterator of keys of the given array.\\n *\\n * @this {!IArrayLike}\\n * @return {!IteratorIterable}\\n */\\n var polyfill = function() {\\n return $jscomp.iteratorFromArray(this, function(i) { return i; });\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/array/of.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require es6/array/from';\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Array.of', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Creates an array from a fixed set of arguments.\\n *\\n *

      Polyfills the static function Array.of(). Does not support\\n * constructor inheritance (i.e. (subclass of Array).of).\\n *\\n * @param {...T} var_args Elements to include in the array.\\n * @return {!Array}\\n * @template T\\n */\\n var polyfill = function(var_args) {\\n return Array.from(arguments);\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/array/values.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require es6/util/iteratorfromarray';\\n'require util/polyfill';\\n\\n// NOTE: Although Array.prototype.values was added to the 2015 edition of the\\n// spec, we consider it an \\\"ES8\\\" feature because many browsers which are\\n// otherwise ES6-compatible, have not implemented it due to web compatibility\\n// issues. See https://bugs.chromium.org/p/chromium/issues/detail?id=615873\\n$jscomp.polyfill('Array.prototype.values', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Returns an iterator of values of the given array.\\n *\\n * @this {!IArrayLike}\\n * @return {!IteratorIterable}\\n * @template VALUE\\n * @suppress {reportUnknownTypes}\\n */\\n var polyfill = function() {\\n return $jscomp.iteratorFromArray(this, function(k, v) { return v; });\\n };\\n\\n return polyfill;\\n}, 'es8', 'es3');\\n\",\"js/es6/async_generator_wrapper.js\":\"/*\\n * Copyright 2018 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Runtime logic for transpiled Async Generators.\\n * @suppress {uselessCode}\\n */\\n'require base';\\n'require es6/promise';\\n'require es6/symbol';\\n'require es6/util/makeasynciterator';\\n\\n\\n/** @enum {number} */\\n$jscomp.AsyncGeneratorWrapper$ActionEnum = {\\n /** Yield the value from the wrapper generator */\\n YIELD_VALUE: 0,\\n /** Yield each value from a delegate generator */\\n YIELD_STAR: 1,\\n /** Resolve the value as a Promise and continue execution */\\n AWAIT_VALUE: 2,\\n};\\n\\n/**\\n * @param {!$jscomp.AsyncGeneratorWrapper$ActionEnum} action\\n * @param {VALUE} value\\n * @constructor\\n * @template VALUE\\n * @struct\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.AsyncGeneratorWrapper$ActionRecord = function(action, value) {\\n /**\\n * @public\\n * @const\\n * @type {!$jscomp.AsyncGeneratorWrapper$ActionEnum}\\n */\\n this.action = action;\\n\\n /**\\n * @public\\n * @const\\n * @type {VALUE}\\n */\\n this.value = /** @type {VALUE} */ (value);\\n};\\n\\n/** @enum {string} */\\n$jscomp.AsyncGeneratorWrapper$GeneratorMethod = {\\n NEXT: 'next', THROW: 'throw', RETURN: 'return',\\n};\\n\\n/**\\n * Records the details of a call to `next()`, `throw()`, or `return()`.\\n *\\n * One of these objects will be created for each call.\\n *\\n * @param {$jscomp.AsyncGeneratorWrapper$GeneratorMethod} method\\n * Method to call on generator\\n * @param {?} param\\n * Parameter for method called on generator\\n *\\n * @param {function(!IIterableResult)} resolve\\n * Function to resolve the Promise associated with this frame.\\n * @param {function(?)} reject\\n * Function to reject the Promise associated with this frame.\\n *\\n * @constructor\\n * @private\\n * @template VALUE\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.AsyncGeneratorWrapper$ExecutionFrame_ = function(\\n method, param, resolve, reject) {\\n /** @type {$jscomp.AsyncGeneratorWrapper$GeneratorMethod} */\\n this.method = method;\\n /** @type {?} */\\n this.param = param;\\n /** @type {function(!IIterableResult)} */\\n this.resolve = resolve;\\n /** @type {function(?)} */\\n this.reject = reject;\\n};\\n\\n/**\\n * @param {!$jscomp.AsyncGeneratorWrapper$ExecutionFrame_} frame\\n * The frame at this position in the queue\\n * @param {$jscomp.AsyncGeneratorWrapper$ExecutionNode_} next\\n * The node containing the frame to be executed after this one completes\\n *\\n * @constructor\\n * @private\\n * @template VALUE\\n */\\n$jscomp.AsyncGeneratorWrapper$ExecutionNode_ = function(frame, next) {\\n /** @type {!$jscomp.AsyncGeneratorWrapper$ExecutionFrame_} */\\n this.frame = frame;\\n /** @type {$jscomp.AsyncGeneratorWrapper$ExecutionNode_} */\\n this.next = next;\\n};\\n\\n/**\\n * A minimalistic queue backed by a linked-list.\\n *\\n * @constructor\\n * @private\\n * @template VALUE\\n */\\n$jscomp.AsyncGeneratorWrapper$ExecutionQueue_ = function() {\\n /**\\n * @type {$jscomp.AsyncGeneratorWrapper$ExecutionNode_}\\n * @private\\n */\\n this.head_ = null;\\n\\n /**\\n *\\n * @type {$jscomp.AsyncGeneratorWrapper$ExecutionNode_}\\n * @private\\n */\\n this.tail_ = null;\\n};\\n\\n/**\\n * @return {boolean}\\n */\\n$jscomp.AsyncGeneratorWrapper$ExecutionQueue_.prototype.isEmpty = function() {\\n return this.head_ === null;\\n};\\n\\n/**\\n * Returns the current head frame if it exists, otherwise throws Error.\\n *\\n * @return {!$jscomp.AsyncGeneratorWrapper$ExecutionFrame_}\\n * @throws {Error} if the queue is empty\\n */\\n$jscomp.AsyncGeneratorWrapper$ExecutionQueue_.prototype.first = function() {\\n if (this.head_) {\\n return this.head_.frame;\\n } else {\\n throw new Error('no frames in executionQueue');\\n }\\n};\\n\\n/**\\n * Drops the current head frame off the head of the queue. Performs same\\n * operations as a theoretical \\\"pop\\\", but saves time by not storing or returning\\n * the popped frame.\\n *\\n * If the queue is empty, no operation is performed.\\n */\\n$jscomp.AsyncGeneratorWrapper$ExecutionQueue_.prototype.drop = function() {\\n if (this.head_) {\\n this.head_ = this.head_.next;\\n if (!this.head_) {\\n this.tail_ = null;\\n }\\n }\\n};\\n\\n/**\\n * @param {!$jscomp.AsyncGeneratorWrapper$ExecutionFrame_} newFrame\\n * the new frame to be executed after all frames currently in the queue\\n */\\n$jscomp.AsyncGeneratorWrapper$ExecutionQueue_.prototype.enqueue = function(\\n newFrame) {\\n var node = new $jscomp.AsyncGeneratorWrapper$ExecutionNode_(newFrame, null);\\n if (this.tail_) {\\n this.tail_.next = node;\\n this.tail_ = node;\\n } else {\\n this.head_ = node;\\n this.tail_ = node;\\n }\\n};\\n\\n/**\\n * @constructor\\n * @implements {AsyncGenerator}\\n * @implements {AsyncIterable}\\n * @template VALUE\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.AsyncGeneratorWrapper = function(\\n /** @type {!Generator<$jscomp.AsyncGeneratorWrapper$ActionRecord>} */\\n generator) {\\n /** @private */\\n this.generator_ = generator;\\n\\n /**\\n * @private\\n * @type {AsyncIterator}\\n */\\n this.delegate_ = null;\\n\\n /**\\n * @type {!$jscomp.AsyncGeneratorWrapper$ExecutionQueue_}\\n * @private\\n */\\n this.executionQueue_ = new $jscomp.AsyncGeneratorWrapper$ExecutionQueue_();\\n\\n $jscomp.initSymbolAsyncIterator();\\n\\n /** @type {$jscomp.AsyncGeneratorWrapper} */\\n this[Symbol.asyncIterator] =\\n /** @return {$jscomp.AsyncGeneratorWrapper} */ function() {\\n return this;\\n };\\n\\n var self = this;\\n\\n /**\\n * @this {undefined}\\n * @param {!IIterableResult} record\\n * @private\\n */\\n this.boundHandleDelegateResult_ = function(record) {\\n self.handleDelegateResult_(record);\\n };\\n\\n /**\\n * @this {undefined}\\n * @param {*} thrownError\\n * @private\\n */\\n this.boundHandleDelegateError_ = function(thrownError) {\\n self.handleDelegateError_(thrownError);\\n };\\n\\n /**\\n * @this {undefined}\\n * @param {*} err\\n * @private\\n */\\n this.boundRejectAndClose_ = function(err) {\\n self.rejectAndClose_(err);\\n };\\n};\\n\\n/**\\n * @param {!$jscomp.AsyncGeneratorWrapper$GeneratorMethod} method\\n * @param {?} param\\n * @return {!Promise>}\\n * @private\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.AsyncGeneratorWrapper.prototype.enqueueMethod_ = function(\\n method, param) {\\n var self = this;\\n return new Promise(function(resolve, reject) {\\n var wasEmpty = self.executionQueue_.isEmpty();\\n self.executionQueue_.enqueue(\\n new $jscomp.AsyncGeneratorWrapper$ExecutionFrame_(\\n method, param, resolve, reject));\\n if (wasEmpty) {\\n self.runFrame_();\\n }\\n });\\n};\\n\\n/**\\n * @override\\n * @param {?=} opt_value\\n * @return {!Promise>}\\n * @suppress {repor"; +a.a+="tUnknownTypes}\\n */\\n$jscomp.AsyncGeneratorWrapper.prototype.next = function(opt_value) {\\n return this.enqueueMethod_(\\n $jscomp.AsyncGeneratorWrapper$GeneratorMethod.NEXT, opt_value);\\n};\\n\\n/**\\n * @override\\n * @param {VALUE} value\\n * @return {!Promise>}\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.AsyncGeneratorWrapper.prototype.return = function(value) {\\n return this.enqueueMethod_(\\n $jscomp.AsyncGeneratorWrapper$GeneratorMethod.RETURN,\\n new $jscomp.AsyncGeneratorWrapper$ActionRecord(\\n $jscomp.AsyncGeneratorWrapper$ActionEnum.YIELD_VALUE, value));\\n};\\n\\n/**\\n * @override\\n * @param {*=} exception\\n * @return {!Promise>}\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.AsyncGeneratorWrapper.prototype.throw = function(exception) {\\n return this.enqueueMethod_(\\n $jscomp.AsyncGeneratorWrapper$GeneratorMethod.THROW, exception);\\n};\\n\\n/**\\n * Recursively executes all frames in the executionQueue until it is empty.\\n * Frames that are added to the queue while execution is being performed will\\n * be executed when they are reached.\\n *\\n * In order to guarantee each frame in the entire queue will be processed\\n * exactly once, each branch in runDelegateFrame and runGeneratorFrame should\\n * conclude with the following specification:\\n *\\n * If the frame is ready to be resolved/rejected:\\n *\\n * 1. Resolve or reject the frame.\\n * 2. Drop the frame from the head of the queue.\\n * 3. End with a call to runFrame.\\n *\\n * Otherwise, if another action must be performed:\\n *\\n * 1. Mutate the frame's method and param to reflect the next action.\\n * 2. End with a call to runFrame.\\n *\\n * @private\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.AsyncGeneratorWrapper.prototype.runFrame_ = function() {\\n if (!this.executionQueue_.isEmpty()) {\\n try {\\n if (this.delegate_) {\\n this.runDelegateFrame_();\\n } else {\\n this.runGeneratorFrame_();\\n }\\n } catch (err) {\\n this.rejectAndClose_(err);\\n }\\n }\\n};\\n\\n/**\\n * For safety, all branches should meet invariants listed in runFrame.\\n *\\n * @private\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.AsyncGeneratorWrapper.prototype.runGeneratorFrame_ = function() {\\n var self = this;\\n var frame = this.executionQueue_.first();\\n try {\\n var genRec = this.generator_[frame.method](frame.param);\\n if (genRec.value instanceof $jscomp.AsyncGeneratorWrapper$ActionRecord) {\\n switch (genRec.value.action) {\\n case $jscomp.AsyncGeneratorWrapper$ActionEnum.YIELD_VALUE:\\n Promise.resolve(genRec.value.value)\\n .then(\\n function(resolvedValue) {\\n frame.resolve({value: resolvedValue, done: genRec.done});\\n self.executionQueue_.drop();\\n self.runFrame_();\\n },\\n function(e) {\\n frame.reject(e);\\n self.executionQueue_.drop();\\n self.runFrame_();\\n })\\n .catch(this.boundRejectAndClose_);\\n return;\\n\\n case $jscomp.AsyncGeneratorWrapper$ActionEnum.YIELD_STAR:\\n self.delegate_ = $jscomp.makeAsyncIterator(genRec.value.value);\\n frame.method = $jscomp.AsyncGeneratorWrapper$GeneratorMethod.NEXT;\\n frame.param = undefined;\\n self.runFrame_();\\n return;\\n\\n case $jscomp.AsyncGeneratorWrapper$ActionEnum.AWAIT_VALUE:\\n Promise.resolve(genRec.value.value)\\n .then(\\n function(resolvedValue) {\\n frame.method =\\n $jscomp.AsyncGeneratorWrapper$GeneratorMethod.NEXT;\\n frame.param = resolvedValue;\\n self.runFrame_();\\n },\\n function(thrownErr) {\\n frame.method =\\n $jscomp.AsyncGeneratorWrapper$GeneratorMethod.THROW;\\n frame.param = thrownErr;\\n self.runFrame_();\\n })\\n .catch(this.boundRejectAndClose_);\\n return;\\n\\n default:\\n throw new Error('Unrecognized AsyncGeneratorWrapper$ActionEnum');\\n }\\n }\\n else {\\n frame.resolve(genRec);\\n self.executionQueue_.drop();\\n self.runFrame_();\\n }\\n } catch (e) {\\n frame.reject(e);\\n self.executionQueue_.drop();\\n self.runFrame_();\\n }\\n};\\n\\n\\n/**\\n * For safety, all branches should meet invariants listed in runFrame.\\n *\\n * @private\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.AsyncGeneratorWrapper.prototype.runDelegateFrame_ = function() {\\n if (!this.delegate_) {\\n throw new Error(\\\"no delegate to perform execution\\\");\\n }\\n var frame = this.executionQueue_.first();\\n if (frame.method in this.delegate_) {\\n try {\\n this.delegate_[frame.method](frame.param)\\n .then(this.boundHandleDelegateResult_, this.boundHandleDelegateError_)\\n .catch(this.boundRejectAndClose_);\\n } catch (err) {\\n this.handleDelegateError_(err);\\n }\\n } else {\\n this.delegate_ = null;\\n this.runFrame_();\\n }\\n};\\n\\n/**\\n * @param {!IIterableResult} record\\n * @private\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.AsyncGeneratorWrapper.prototype.handleDelegateResult_ = function(\\n record) {\\n var frame = this.executionQueue_.first();\\n if (record.done === true) {\\n // Delegate is done. Its return value becomes the value of the `yield*`\\n // expression. We must continue the async generator as if next() were called\\n // with that value here.\\n this.delegate_ = null;\\n frame.method = $jscomp.AsyncGeneratorWrapper$GeneratorMethod.NEXT;\\n frame.param = record.value;\\n this.runFrame_();\\n } else {\\n frame.resolve({value: record.value, done: false});\\n this.executionQueue_.drop();\\n this.runFrame_();\\n }\\n};\\n\\n/**\\n * @param {*} thrownError\\n * @private\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.AsyncGeneratorWrapper.prototype.handleDelegateError_ = function(\\n thrownError) {\\n var frame = this.executionQueue_.first();\\n // The delegate threw an exception or rejected a promise, so we must continue\\n // our generator as if the `yield *` threw the exception.\\n this.delegate_ = null;\\n frame.method = $jscomp.AsyncGeneratorWrapper$GeneratorMethod.THROW;\\n frame.param = thrownError;\\n this.runFrame_();\\n};\\n\\n/**\\n * Rejects the current frame and closes the generator.\\n *\\n * @param {*} err Error causing the rejection\\n * @private\\n */\\n$jscomp.AsyncGeneratorWrapper.prototype.rejectAndClose_ = function(err) {\\n if (!this.executionQueue_.isEmpty()) {\\n this.executionQueue_.first().reject(err);\\n this.executionQueue_.drop();\\n }\\n\\n if (this.delegate_ && 'return' in this.delegate_) {\\n this.delegate_['return'](undefined);\\n this.delegate_ = null;\\n }\\n this.generator_['return'](undefined);\\n\\n // Keep processing all frames remaining in the queue.\\n // Note: Some of these frames might be throw requests, but our backing\\n // generator will handle these appropriately.\\n this.runFrame_();\\n};\\n\",\"js/es6/conformance.js\":\"/*\\n * Copyright 2017 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/global';\\n\\n\\n/**\\n * @fileoverview Check one of the most obscure features of ES6 as a proxy\\n * for full conformance. If this is enabled, this test is used instead of\\n * larger, more specific conformance tests.\\n */\\n\\n/**\\n * Check ES6 conformance by checking an obscure detail of Proxy that\\n * wasn't implemented correctly until after all other ES6 features in\\n * most browsers.\\n * @return {boolean} Whether Proxy works correctly.\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.checkEs6ConformanceViaProxy = function() {\\n try {\\n var proxied = {};\\n var proxy = Object.create(new $jscomp.global['Proxy'](proxied, {\\n 'get': function (target, key, receiver) {\\n return target == proxied && key == 'q' && receiver == proxy;\\n }\\n }));\\n return proxy['q'] === true;\\n } catch (err) {\\n return false;\\n }\\n};\\n\\n/**\\n * If this is true, assume that a runtime which implements Proxy also\\n * implements the rest of the ECMAScript 2015 spec.\\n * @define {boolean}\\n */\\n$jscomp.USE_PROXY_FOR_ES6_CONFORMANCE_CHECKS = false;\\n\\n/**\\n * Whether the runtime implements the entire ECMAScript 2015 spec.\\n * @const {boolean}\\n */\\n$jscomp.ES6_CONFORMANCE =\\n $jscomp.USE_PROXY_FOR_ES6_CONFORMANCE_CHECKS &&\\n $jscomp.checkEs6ConformanceViaProxy();\\n\",\"js/es6/execute_async_generator.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require base';\\n'require es6/promise';\\n'require es6/generator_engine';\\n\\n/**\\n * Handles the execution of an async function.\\n *\\n * An async function, foo(a, b), will be rewritten as:\\n *\\n * ```\\n * function foo(a, b) {\\n * let $jscomp$async$this = this;\\n * let $jscomp$async$arguments = arguments;\\n * let $jscomp$async$super$get$x = () => super.x;\\n * function* $jscomp$async$generator() {\\n * // original body of foo() with:\\n * // - await (x) replaced with yield (x)\\n * // - arguments replaced with $jscomp$async$arguments\\n * // - this replaced with $jscomp$async$this\\n * // - super.x replaced with $jscomp$async$super$get$x()\\n * // - super.x(5) replaced with $jscomp$async$super$get$x()\\n * // .call($jscomp$async$this, 5)\\n * }\\n * return $jscomp.executeAsyncGenerator($jscomp$async$generator());\\n * }\\n * ```\\n * @param {!Generator} generator\\n * @return {!Promise}\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.asyncExecutePromiseGenerator = function(generator) {\\n function passValueToGenerator(value) {\\n return generator.next(value);\\n }\\n\\n function passErrorToGenerator(error) {\\n return generator.throw(error);\\n }\\n\\n return new Promise(function(resolve, reject) {\\n function handleGeneratorRecord(/** !IIterableResult<*> */ genRec) {\\n if (genRec.done) {\\n resolve(genRec.value);\\n } else {\\n // One can await a non-promise, so genRec.value\\n // might not already be a promise.\\n Promise.resolve(genRec.value)\\n .then(passValueToGenerator, passErrorToGenerator)\\n .then(handleGeneratorRecord, reject);\\n }\\n }\\n\\n handleGeneratorRecord(generator.next());\\n });\\n};\\n\\n/**\\n * Handles the execution of a generator function returning promises.\\n *\\n * An async function, foo(a, b), will be rewritten as:\\n *\\n * ```\\n * function foo(a, b) {\\n * let $jscomp$async$this = this;\\n * let $jscomp$async$arguments = arguments;\\n * let $jscomp$async$super$get$x = () => super.x;\\n * return $jscomp.asyncExecutePromiseGeneratorFunction(\\n * function* () {\\n * // original body of foo() with:\\n * // - await (x) replaced with yield (x)\\n * // - arguments replaced with $jscomp$async$arguments\\n * // - this replaced with $jscomp$async$this\\n * // - super.x replaced with $jscomp$async$super$get$x()\\n * // - super.x(5) replaced with $jscomp$async$super$get$x()\\n * // .call($jscomp$async$this, 5)\\n * });\\n * }\\n * ```\\n * @param {function(): !Generator} generatorFunction\\n * @return {!Promise}\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.asyncExecutePromiseGeneratorFunction = function(generatorFunction) {\\n return $jscomp.asyncExecutePromiseGenerator(generatorFunction());\\n};\\n\\n/**\\n * Handles the execution of a state machine program that represents transpiled\\n * async function.\\n *\\n * @final\\n * @param {function(!$jscomp.generator.Context): (void|{value: ?})} program\\n * @return {!Promise}\\n * @suppress {reportUnknownTypes, visibility}\\n */\\n$jscomp.asyncExecutePromiseGeneratorProgram = function(program) {\\n return $jscomp.asyncExecutePromiseGenerator(\\n new $jscomp.generator.Generator_(\\n new $jscomp.generator.Engine_(\\n program)));\\n};\\n\",\"js/es6/generator_engine.js\":\"/*\\n * Copyright 2018 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require base';\\n'require es6/symbol';\\n'require es6/util/setprototypeof';\\n'require es6/util/makeiterator';\\n\\n/**\\n * @fileoverview Implementation for $jscomp.generator\\n *\\n * This closure-compiler internal JavaScript library provides an ES3-compatible\\n * API for writing generator functions using a minimum of boilerplate.\\n *\\n * Example:\\n * ```javascript\\n * // yields numbers starting with the given value, then incrementing by the\\n * // value supplied to the next() method until the computed value is <= min or\\n * // >= max. Then it returns the total number of times it yielded.\\n * // If the client code calls throw(), the error will be logged and then\\n * // yielded, but the generator won't terminate.\\n * function *es6Definition(start, min, max) {\\n * let currentValue = start;\\n * let yieldCount = 0;\\n * while (currentValue > min && currentValue < max) {\\n * try {\\n * currentValue += yield(currentValue);\\n * } catch (e) {\\n * yield(e);\\n * console.log('client threw error', e);\\n * } finally {\\n * yieldCount++;\\n * }\\n * }\\n * return [yieldCount, currentValue];\\n * }\\n *\\n * function es3Definition(start, min, max) {\\n * var currentValue;\\n * var yieldCount;\\n * var e;\\n *\\n * return $jscomp.generator.createGenerator(\\n * es3Definition,\\n * function (context$) {\\n * switch (context$.nextAddress) {\\n * case 1: // execution always starts with 1\\n * currentValue = start;\\n * yieldCount = 0;\\n * // fall-through\\n *\\n * case 2:\\n * if (!(currentValue > min && currentValue < max)) {\\n * // exit while loop:\\n * return context$.jumpTo(3);\\n * }\\n * // try {\\n * JSCompiler_temp_const$jscomp$1 = currentValue;\\n * context$.setCatchFinallyBlocks(4, 5);\\n * return context$.yield(currentValue, 7);\\n *\\n * case 7:\\n * currentValue =\\n * JSCompiler_temp_const$jscomp$1 + context$.yieldResult;\\n * // fall-through: execute finally block\\n *\\n * case 5: // finally block start\\n * context$.enterFinallyBlock();\\n * yieldCount++;\\n * return context$.leaveFinallyBlock(6);\\n *\\n * case 4: // catch block start\\n * e = context$.enterCatchBlock();\\n * return context$.yield(e, 8);\\n *\\n * case 8: // finish catch block\\n *"; +a.a+=" console.log('client threw error', e);\\n * return context$.jumpTo(5);\\n *\\n * case 6:\\n * context$.jumpTo(2);\\n * break;\\n *\\n * case 3:\\n * // come back here when while loop block exits\\n * return context$.return([yieldCount, currentValue]);\\n * }\\n * }\\n * });\\n * };\\n * ```\\n */\\n\\n/** @const */\\n$jscomp.generator = {};\\n\\n/**\\n * Ensures that the iterator result is actually an object.\\n *\\n * @private\\n * @final\\n * @param {*} result\\n * @return {void}\\n * @throws {TypeError} if the result is not an instenace of Object.\\n */\\n$jscomp.generator.ensureIteratorResultIsObject_ = function(result) {\\n if (result instanceof Object) {\\n return;\\n }\\n throw new TypeError('Iterator result ' + result + ' is not an object');\\n};\\n\\n\\n/**\\n * Tracks state machine state used by generator.Engine.\\n *\\n * @template VALUE\\n * @constructor\\n * @final\\n * @struct\\n */\\n$jscomp.generator.Context = function() {\\n /**\\n * Whether the generator program is being executed at the moment in the\\n * current context. Is used to prevent reentrancy.\\n *\\n * @private\\n * @type {boolean}\\n */\\n this.isRunning_ = false;\\n\\n /**\\n * An iterator that should yield all its values before the main program can\\n * continue.\\n *\\n * @private\\n * @type {?Iterator}\\n */\\n this.yieldAllIterator_ = null;\\n\\n /**\\n * The value that will be sent to the program as the result of suspended\\n * yield expression.\\n *\\n * @type {?}\\n */\\n this.yieldResult = undefined;\\n\\n /**\\n * The next address where the state machine execution should be resumed.\\n *\\n *

      Program execution starts at 1 and ends at 0.\\n *\\n * @type {number}\\n */\\n this.nextAddress = 1;\\n\\n /**\\n * The address that should be executed once an exception is thrown.\\n *\\n *

      Value of 0 means no catch block exist that would handles an exception.\\n *\\n * @private\\n * @type {number}\\n */\\n this.catchAddress_ = 0;\\n\\n /**\\n * The address that should be executed once the result is being returned\\n * or if the exception is thrown and there is no catchAddress specified.\\n *\\n *

      Value of 0 means no finally block is set.\\n *\\n * @private\\n * @type {number}\\n */\\n this.finallyAddress_ = 0;\\n\\n /**\\n * Stores information for the runtime propagation of values and control\\n * flow such as the behaviour of statements (break, continue, return and\\n * throw) that perform nonlocal transfers of control.\\n *\\n * @private\\n * @type {null|{return: VALUE}|{exception, isException: boolean}|{jumpTo: number}}.\\n */\\n this.abruptCompletion_ = null;\\n\\n /**\\n * The preserved abruptCompletion_ when entering a `finally` block. If\\n * the `finally` block completes normally the preserved abruptCompletion_ is\\n * restored:\\n *

      \\n   * try {\\n   * } finally {  // nesting level 0\\n   *   // abruptCompletion_ is saved in finallyContexts_[0]\\n   *   try {\\n   *   } finally {  // nesting level 1\\n   *     // abruptCompletion_ is saved in finallyContexts_[1]\\n   *     ...\\n   *     // abruptCompletion_ is restored from finallyContexts_[1]\\n   *   }\\n   *   // abruptCompletion_ is restored from finallyContexts_[0]\\n   * }\\n   * 
      \\n   *\\n   * @private\\n   * @type {?Array}.\\n   */\\n  this.finallyContexts_ = null;\\n};\\n\\n/**\\n * Marks generator program as being run.\\n *\\n * @private\\n * @final\\n * @return {void}\\n * @throws {TypeError} if generator is already running.\\n */\\n$jscomp.generator.Context.prototype.start_ = function() {\\n  if (this.isRunning_) {\\n    throw new TypeError('Generator is already running');\\n  }\\n  this.isRunning_ = true;\\n};\\n\\n/**\\n *\\n *\\n * @private\\n * @final\\n * @return {void}\\n */\\n$jscomp.generator.Context.prototype.stop_ = function() {\\n  this.isRunning_ = false;\\n};\\n\\n/**\\n * Transfers program execution to an appropriate catch/finally block that\\n * should be executed if exception occurs.\\n *\\n * @private\\n * @final\\n * @return {void}\\n */\\n$jscomp.generator.Context.prototype.jumpToErrorHandler_ = function() {\\n  this.nextAddress = this.catchAddress_ || this.finallyAddress_;\\n};\\n\\n/**\\n * Sets the result of suspended yield expression.\\n *\\n * @private\\n * @final\\n * @param {?=} value The value to send to the generator.\\n * @return {void}\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.generator.Context.prototype.next_ = function(value) {\\n  this.yieldResult = value;\\n};\\n\\n/**\\n * Throws exception as the result of suspended yield.\\n *\\n * @private\\n * @final\\n * @param {?} e\\n * @return {void}\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.generator.Context.prototype.throw_ = function(e) {\\n  this.abruptCompletion_ = {exception: e, isException: true};\\n  this.jumpToErrorHandler_();\\n};\\n\\n/**\\n * Returns a value as the result of generator function.\\n *\\n * @final\\n * @param {VALUE=} value\\n * @return {void}\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.generator.Context.prototype.return = function(value) {\\n  this.abruptCompletion_ = {return: /** @type {VALUE} */ (value)};\\n  this.nextAddress = this.finallyAddress_;\\n};\\n\\n/**\\n * Changes the context so the program execution will continue from the given\\n * state after executing nessesary pending finally blocks first.\\n *\\n * @final\\n * @param {number} nextAddress The state that should be run.\\n * @return {void}\\n */\\n$jscomp.generator.Context.prototype.jumpThroughFinallyBlocks = function(\\n    nextAddress) {\\n  this.abruptCompletion_ = {jumpTo: nextAddress};\\n  this.nextAddress = this.finallyAddress_;\\n};\\n\\n/**\\n * Pauses the state machine program assosiated with generator function to yield\\n * a value.\\n *\\n * @final\\n * @param {VALUE} value The value to return from the generator function via\\n *     the iterator protocol.\\n * @param {number} resumeAddress The address where the program should resume.\\n * @return {{value: VALUE}}\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.generator.Context.prototype.yield = function(value, resumeAddress) {\\n  this.nextAddress = resumeAddress;\\n  return {value: value};\\n};\\n\\n/**\\n * Causes the state machine program to yield all values from an iterator.\\n *\\n * @final\\n * @param {string|!Iterator|!Iterable|!Arguments} iterable\\n *     Iterator to yeild all values from.\\n * @param {number} resumeAddress The address where the program should resume.\\n * @return {void | {value: VALUE}}\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.generator.Context.prototype.yieldAll = function(\\n    iterable, resumeAddress) {\\n  /** @const @type {!Iterator} */ var iterator =\\n      $jscomp.makeIterator(iterable);\\n  /** @const */ var result = iterator.next();\\n  $jscomp.generator.ensureIteratorResultIsObject_(result);\\n  if (result.done) {\\n    // If `someGenerator` in `x = yield *someGenerator` completes immediately,\\n    // x is the return value of that generator.\\n    this.yieldResult = result.value;\\n    this.nextAddress = resumeAddress;\\n    return;\\n  }\\n  this.yieldAllIterator_ = iterator;\\n  return this.yield(result.value, resumeAddress);\\n};\\n\\n/**\\n * Changes the context so the program execution will continue from the given\\n * state.\\n *\\n * @final\\n * @param {number} nextAddress The state the program should continue\\n * @return {void}\\n */\\n$jscomp.generator.Context.prototype.jumpTo = function(nextAddress) {\\n  this.nextAddress = nextAddress;\\n};\\n\\n/**\\n * Changes the context so the program execution ends.\\n *\\n * @final\\n * @return {void}\\n */\\n$jscomp.generator.Context.prototype.jumpToEnd = function() {\\n  this.nextAddress = 0;\\n};\\n\\n/**\\n * Sets catch / finally handlers.\\n * Used for try statements with catch blocks.\\n *\\n * @final\\n * @param {number} catchAddress The address of the catch block.\\n * @param {number=} finallyAddress The address of the finally block.\\n * @return {void}\\n */\\n$jscomp.generator.Context.prototype.setCatchFinallyBlocks = function(\\n    catchAddress, finallyAddress) {\\n  this.catchAddress_ = catchAddress;\\n  if (finallyAddress != undefined) {\\n    this.finallyAddress_ = finallyAddress;\\n  }\\n};\\n\\n/**\\n * Sets finally handler.\\n * Used for try statements without catch blocks.\\n *\\n * @const\\n * @param {number=} finallyAddress The address of the finally block or 0.\\n * @return {void}\\n */\\n$jscomp.generator.Context.prototype.setFinallyBlock = function(finallyAddress) {\\n  this.catchAddress_ = 0;\\n  this.finallyAddress_ = finallyAddress || 0;\\n};\\n\\n/**\\n * Sets a catch handler and jumps to the next address.\\n * Used for try statements without finally blocks.\\n *\\n * @final\\n * @param {number} nextAddress The state that should be run next.\\n * @param {number=} catchAddress The address of the catch block or 0.\\n * @return {void}\\n */\\n$jscomp.generator.Context.prototype.leaveTryBlock = function(\\n    nextAddress, catchAddress) {\\n  this.nextAddress = nextAddress;\\n  this.catchAddress_ = catchAddress || 0;\\n};\\n\\n/**\\n * Initializes exception variable in the beginning of catch block.\\n *\\n * @final\\n * @param {number=} nextCatchBlockAddress The address of the next catch block\\n *     that is preceded by no finally blocks.\\n * @return {?} Returns an exception that was thrown from \\\"try\\\" block.\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.generator.Context.prototype.enterCatchBlock = function(\\n    nextCatchBlockAddress) {\\n  this.catchAddress_ = nextCatchBlockAddress || 0;\\n  /** @const */ var exception =\\n      /** @type {{exception, isException: boolean}} */ (this.abruptCompletion_)\\n          .exception;\\n  this.abruptCompletion_ = null;\\n  return exception;\\n};\\n\\n/**\\n * Saves the current throw context which will be restored at the end of finally\\n * block.\\n *\\n * @final\\n * @param {number=} nextCatchAddress\\n * @param {number=} nextFinallyAddress\\n * @param {number=} finallyDepth The nesting level of current \\\"finally\\\" block.\\n * @return {void}\\n */\\n$jscomp.generator.Context.prototype.enterFinallyBlock = function(\\n    nextCatchAddress, nextFinallyAddress, finallyDepth) {\\n  if (!finallyDepth) {\\n    this.finallyContexts_ = [this.abruptCompletion_];\\n  } else {\\n    /**\\n     * @type {!Array}\\n     */\\n    (this.finallyContexts_)[finallyDepth] = this.abruptCompletion_;\\n  }\\n  this.catchAddress_ = nextCatchAddress || 0;\\n  this.finallyAddress_ = nextFinallyAddress || 0;\\n};\\n\\n/**\\n * Figures out whether the program execution should continue normally, or jump\\n * to the closest catch/finally block.\\n *\\n * @final\\n * @param {number} nextAddress The state that should be run next.\\n * @param {number=} finallyDepth The nesting level of current \\\"finally\\\" block.\\n * @return {void}\\n * @suppress {strictMissingProperties}\\n */\\n$jscomp.generator.Context.prototype.leaveFinallyBlock = function(\\n    nextAddress, finallyDepth) {\\n  // There could be trailing finally contexts if a nested finally throws an\\n  // exception or return.\\n  // e.g.\\n  // try {\\n  //   ...\\n  //   return 1;\\n  // } finally {\\n  //   // finallyDepth == 0\\n  //   // finallyContext == [{return: 1}]\\n  //   try {\\n  //     ...\\n  //     try {\\n  //       throw new Error(2);\\n  //     } finally {\\n  //       // finallyDepth == 1\\n  //       // finallyContext == [{return: 1}, {exception: Error(2)}]\\n  //       try {\\n  //         throw new Error(3);\\n  //       } finally {\\n  //         // finallyDepth == 2\\n  //         // finallyContext == [\\n  //         //     {return: 1},\\n  //         //     {exception: Error(2)},\\n  //         //     {exception: Error(3)}\\n  //         // ]\\n  //         throw new Error(4); // gets written in abruptCompletion_\\n  //         // leaveFinallyBlock() never gets called here\\n  //       }\\n  //       // leaveFinallyBlock() never gets called here\\n  //     }\\n  //   } catch (e) {\\n  //      // swallow error\\n  //      // abruptCompletion becomes null\\n  //   } finally {\\n  //     // finallyDepth == 1\\n  //     // finallyContext == [\\n  //     //     {return: 1},\\n  //     //     null, // overwritten, because catch swallowed the error\\n  //     //     {exception: Error(3)}  // left over\\n  //     // ]\\n  //     // leaveFinallyBlock() called here\\n  //     // finallyContext == [{return: 1}]\\n  //     // abruptCompletion == null\\n  //   }\\n  //   // leaveFinallyBlock() called here\\n  //   // finallyContext = []\\n  //   // abruptCompletion == {return: 1};\\n  // }\\n  /** @const */ var preservedContext =\\n      /**\\n       * @type {!Array}\\n       */\\n      (this.finallyContexts_).splice(finallyDepth || 0)[0];\\n  /** @const */ var abruptCompletion = this.abruptCompletion_ =\\n      this.abruptCompletion_ || preservedContext;\\n  if (abruptCompletion) {\\n    if (abruptCompletion.isException) {\\n      return this.jumpToErrorHandler_();\\n    }\\n    // Check if there is a pending break/continue jump that is not preceded by\\n    // finally blocks that should be executed before.\\n    // We always generate case numbers for the start and end of loops before\\n    // numbers for anything they contain, so any finally blocks within will be\\n    // guaranteed to have higher addresses than the loop break and continue\\n    // positions.\\n    // e.g.\\n    // l1: while (...) {            // generated addresses: 100: break l1;\\n    //       try {                  // generated addresses: 101: finally,\\n    //         try {                // generated addresses: 102: finally,\\n    //           l2: while (...) {  // generated addresses: 103: break l2;\\n    //\\n    //                 if (...) {\\n    //                   break l1;  // becomes\\n    //                              // $context.jumpThroughFinallyBlocks(101),\\n    //                              // since 2 finally blocks must be crossed\\n    //                 }\\n    //                 break l2;    // becomes $context.jumpTo(103)\\n    //               }\\n    //         } finally {\\n    //           // When leaving this finally block:\\n    //           // 1. We keep the abrupt completion indicating 'break l1'\\n    //           // 2. We jump to the enclosing finally block.\\n    //         }\\n    //       } finally {\\n    //         // When leaving this finally block:\\n    //         // 1. We complete the abruptCompletion indicating 'break l1' by\\n    //         //   jumping to the loop start address.\\n    //         // 2. Abrupt completion is now null, so normal execution\\n    //         //   continues from there.\\n    //       }\\n    //     }\\n    if (abruptCompletion.jumpTo != undefined &&\\n        this.finallyAddress_ < abruptCompletion.jumpTo) {\\n      this.nextAddress = abruptCompletion.jumpTo;\\n      this.abruptCompletion_ = null;\\n    } else {\\n      this.nextAddress = this.finallyAddress_;\\n    }\\n  } else {\\n    this.nextAddress = nextAddress;\\n  }\\n};\\n\\n/**\\n * Is used in transpilation of `for in` statements.\\n *\\n * 

      for (var i in obj) {...} becomes:\\n *

      \\n * for (var i, $for$in = context$.forIn(obj);\\n *      (i = $for$in.getNext()) != null;\\n *      ) {\\n *   ...\\n * }\\n * 
      \\n *\\n * @final\\n * @param {?} object\\n * @return {!$jscomp.generator.Context.PropertyIterator}\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.generator.Context.prototype.forIn = function(object) {\\n return new $jscomp.generator.Context.PropertyIterator(object);\\n};\\n\\n/**\\n * @constructor\\n * @final\\n * @struct\\n * @param {?} object\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.generator.Context.PropertyIterator = function(object) {\\n /**\\n * @private\\n * @const\\n * @type {?}\\n */\\n this.object_ = object;\\n\\n /**\\n * @private\\n * @const\\n * @type {!Array}\\n */\\n this.properties_ = [];\\n\\n for (var property in /** @type {!Object} */ (object)) {\\n this.properties_.push(property);\\n }\\n this.properties_.reverse();\\n};\\n\\n/**\\n * Returns the next object's property that is still valid.\\n *\\n * @final\\n * @return {?string}\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.generator.Context.PropertyIterator.prototype.getNext = function() {\\n // The JS spec does not require that properties added after the loop begins\\n // be included in the loop, but it does require that the cur"; +a.a+="rent property\\n // must still exist on the object when the loop iteration starts.\\n while (this.properties_.length > 0) {\\n /** @const */ var property = this.properties_.pop();\\n if (property in /** @type {!Object} */ (this.object_)) {\\n return property;\\n }\\n }\\n return null;\\n};\\n\\n/**\\n * Engine handling execution of a state machine associated with the generator\\n * program and its context.\\n *\\n * @private\\n * @template VALUE\\n * @constructor\\n * @final\\n * @struct\\n * @param {function(!$jscomp.generator.Context): (void|{value: VALUE})} program\\n */\\n$jscomp.generator.Engine_ = function(program) {\\n /**\\n * @private\\n * @const\\n * @type {!$jscomp.generator.Context}\\n */\\n this.context_ = new $jscomp.generator.Context();\\n\\n /**\\n * @private\\n * @const\\n * @type {function(!$jscomp.generator.Context): (void|{value: VALUE})}\\n */\\n this.program_ = program;\\n};\\n\\n/**\\n * Returns an object with two properties done and value.\\n * You can also provide a parameter to the next method to send a value to the\\n * generator.\\n *\\n * @private\\n * @final\\n * @param {?=} value The value to send to the generator.\\n * @return {!IIterableResult}\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.generator.Engine_.prototype.next_ = function(value) {\\n this.context_.start_();\\n if (this.context_.yieldAllIterator_) {\\n return this.yieldAllStep_(\\n this.context_.yieldAllIterator_.next, value, this.context_.next_);\\n }\\n this.context_.next_(value);\\n return this.nextStep_();\\n};\\n\\n/**\\n * Attempts to finish the generator with a given value.\\n *\\n * @private\\n * @final\\n * @param {VALUE} value The value to return.\\n * @return {!IIterableResult}\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.generator.Engine_.prototype.return_ = function(value) {\\n this.context_.start_();\\n /** @const */ var yieldAllIterator = this.context_.yieldAllIterator_;\\n if (yieldAllIterator) {\\n /** @const @type {function(VALUE): !IIterableResult} */ var\\n returnFunction =\\n 'return' in yieldAllIterator ? yieldAllIterator['return'] :\\n function(v) {\\n return {value: v, done: true};\\n };\\n return this.yieldAllStep_(returnFunction, value, this.context_.return);\\n }\\n this.context_.return(value);\\n return this.nextStep_();\\n};\\n\\n/**\\n * Resumes the execution of a generator by throwing an error into it and\\n * returns an object with two properties done and value.\\n *\\n * @private\\n * @final\\n * @param {?} exception The exception to throw.\\n * @return {!IIterableResult}\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.generator.Engine_.prototype.throw_ = function(exception) {\\n this.context_.start_();\\n if (this.context_.yieldAllIterator_) {\\n return this.yieldAllStep_(\\n this.context_.yieldAllIterator_['throw'], exception,\\n this.context_.next_);\\n }\\n this.context_.throw_(exception);\\n return this.nextStep_();\\n};\\n\\n/**\\n * Redirects next/throw/return method calls to an iterator passed to \\\"yield *\\\".\\n *\\n * @private\\n * @final\\n * @template T\\n * @param {function(this:Iterator, T): !IIterableResult} action\\n * @param {T} value\\n * @param {function(this:$jscomp.generator.Context, VALUE): void} nextAction\\n * @return {!IIterableResult}\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.generator.Engine_.prototype.yieldAllStep_ = function(\\n action, value, nextAction) {\\n try {\\n /** @const */ var result = action.call(\\n /** @type {!Iterator} */ (this.context_.yieldAllIterator_),\\n value);\\n $jscomp.generator.ensureIteratorResultIsObject_(result);\\n if (!result.done) {\\n this.context_.stop_();\\n return result;\\n }\\n // After `x = yield *someGenerator()` x is the return value of the\\n // generator, not a value passed to this generator by the next() method.\\n /** @const */ var resultValue = result.value;\\n } catch (e) {\\n this.context_.yieldAllIterator_ = null;\\n this.context_.throw_(e);\\n return this.nextStep_();\\n }\\n this.context_.yieldAllIterator_ = null;\\n nextAction.call(this.context_, resultValue);\\n return this.nextStep_();\\n};\\n\\n/**\\n * Continues/resumes program execution until the next suspension point (yield).\\n *\\n * @private\\n * @final\\n * @return {!IIterableResult}\\n * @suppress {reportUnknownTypes, strictMissingProperties}\\n */\\n$jscomp.generator.Engine_.prototype.nextStep_ = function() {\\n while (this.context_.nextAddress) {\\n try {\\n /** @const */ var yieldValue = this.program_(this.context_);\\n if (yieldValue) {\\n this.context_.stop_();\\n return {value: yieldValue.value, done: false};\\n }\\n } catch (e) {\\n this.context_.yieldResult = undefined;\\n this.context_.throw_(e);\\n }\\n }\\n\\n this.context_.stop_();\\n if (this.context_.abruptCompletion_) {\\n /** @const */ var abruptCompletion = this.context_.abruptCompletion_;\\n this.context_.abruptCompletion_ = null;\\n if (abruptCompletion.isException) {\\n throw abruptCompletion.exception;\\n }\\n return {value: abruptCompletion.return, done: true};\\n }\\n return {value: /** @type {?} */ (undefined), done: true};\\n};\\n\\n/**\\n * The Generator object that is returned by a generator function and it\\n * conforms to both the iterable protocol and the iterator protocol.\\n *\\n * @private\\n * @template VALUE\\n * @constructor\\n * @final\\n * @implements {Generator}\\n * @param {!$jscomp.generator.Engine_} engine\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.generator.Generator_ = function(engine) {\\n /** @const @override */\\n this.next = function(opt_value) {\\n return engine.next_(opt_value);\\n };\\n\\n /** @const @override */\\n this.throw = function(exception) {\\n return engine.throw_(exception);\\n };\\n\\n /** @const @override */\\n this.return = function(value) {\\n return engine.return_(value);\\n };\\n\\n $jscomp.initSymbolIterator();\\n\\n /** @this {$jscomp.generator.Generator_} */\\n this[Symbol.iterator] = function() {\\n return this;\\n };\\n\\n // TODO(skill): uncomment once Symbol.toStringTag is polyfilled:\\n // this[Symbol.toStringTag] = 'Generator';\\n};\\n\\n/**\\n * Creates a generator backed up by Engine running a given program.\\n *\\n * @final\\n * @template VALUE\\n * @param {function(this:?, ...): (!Iterator|!Iterable)} generator\\n * @param {function(!$jscomp.generator.Context): (void|{value: VALUE})} program\\n * @return {!Generator}\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.generator.createGenerator = function(generator, program) {\\n /** @const */ var result =\\n new $jscomp.generator.Generator_(new $jscomp.generator.Engine_(program));\\n // The spec says that `myGenFunc() instanceof myGenFunc` must be true.\\n // We'll make this work by setting the prototype before calling the\\n // constructor every time. All of the methods of the object are defined on the\\n // instance by the constructor, so this does no harm.\\n // We also cast Generator_ to Object to hide dynamic inheritance from\\n // jscompiler, it makes ConformanceRules$BanUnknownThis happy.\\n if ($jscomp.setPrototypeOf) {\\n /** @type {function(!Object, ?Object): !Object} */ ($jscomp.setPrototypeOf)(\\n result, generator.prototype);\\n }\\n return result;\\n};\\n\",\"js/es6/map.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require es6/conformance';\\n'require es6/symbol';\\n'require es6/util/makeiterator';\\n'require es6/weakmap';\\n'require util/defines';\\n'require util/owns';\\n'require util/polyfill';\\n\\n\\n/**\\n * Internal record type for entries.\\n * @record\\n * @template KEY, VALUE\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.MapEntry = function() {\\n /** @type {!$jscomp.MapEntry} */\\n this.previous;\\n /** @type {!$jscomp.MapEntry} */\\n this.next;\\n /** @type {?Object} */\\n this.head;\\n /** @type {KEY} */\\n this.key;\\n /** @type {VALUE} */\\n this.value;\\n};\\n\\n\\n$jscomp.polyfill('Map',\\n /**\\n * @param {*} NativeMap\\n * @return {*}\\n * @suppress {reportUnknownTypes}\\n */\\n function(NativeMap) {\\n\\n /**\\n * Checks conformance of the existing Map.\\n * @return {boolean} True if the browser's implementation conforms.\\n * @suppress {missingProperties} \\\"entries\\\" unknown prototype\\n */\\n function isConformant() {\\n if ($jscomp.ASSUME_NO_NATIVE_MAP ||\\n !NativeMap ||\\n typeof NativeMap != \\\"function\\\" ||\\n !NativeMap.prototype.entries ||\\n typeof Object.seal != 'function') {\\n return false;\\n }\\n // Some implementations don't support constructor arguments.\\n try {\\n NativeMap = /** @type {function(new: Map, !Iterator=)} */ (NativeMap);\\n var key = Object.seal({x: 4});\\n var map = new NativeMap($jscomp.makeIterator([[key, 's']]));\\n if (map.get(key) != 's' || map.size != 1 || map.get({x: 4}) ||\\n map.set({x: 4}, 't') != map || map.size != 2) {\\n return false;\\n }\\n var /** !Iterator */ iter = map.entries();\\n var item = iter.next();\\n if (item.done || item.value[0] != key || item.value[1] != 's') {\\n return false;\\n }\\n item = iter.next();\\n if (item.done || item.value[0].x != 4 ||\\n item.value[1] != 't' || !iter.next().done) {\\n return false;\\n }\\n return true;\\n } catch (err) { // This should hopefully never happen, but let's be safe.\\n return false;\\n }\\n }\\n\\n if ($jscomp.USE_PROXY_FOR_ES6_CONFORMANCE_CHECKS) {\\n if (NativeMap && $jscomp.ES6_CONFORMANCE) return NativeMap;\\n } else {\\n if (isConformant()) return NativeMap;\\n }\\n\\n // We depend on Symbol.iterator, so ensure it's loaded.\\n $jscomp.initSymbolIterator();\\n\\n\\n /** @const {!WeakMap} */\\n var idMap = new WeakMap();\\n\\n\\n /**\\n * Polyfill for the global Map data type.\\n * @constructor\\n * @struct\\n * @extends {Map}\\n * @implements {Iterable>}\\n * @template KEY, VALUE\\n * @param {!Iterable>|!Array>|null=}\\n * opt_iterable Optional data to populate the map.\\n */\\n // TODO(sdh): fix param type if heterogeneous arrays ever supported.\\n var PolyfillMap = function(opt_iterable) {\\n /** @private {!Object>>} */\\n this.data_ = {};\\n\\n /** @private {!$jscomp.MapEntry} */\\n this.head_ = createHead();\\n\\n // Note: this property should not be changed. If we're willing to give up\\n // ES3 support, we could define it as a property directly. It should be\\n // marked readonly if such an annotation ever comes into existence.\\n /** @type {number} */\\n this.size = 0;\\n\\n if (opt_iterable) {\\n var iter = $jscomp.makeIterator(opt_iterable);\\n var entry;\\n while (!(entry = iter.next()).done) {\\n var item =\\n /** @type {!IIterableResult>} */ (entry).value;\\n this.set(/** @type {KEY} */ (item[0]), /** @type {VALUE} */ (item[1]));\\n }\\n }\\n };\\n\\n\\n /** @override */\\n PolyfillMap.prototype.set = function(key, value) {\\n // normalize -0/+0 to +0\\n key = key === 0 ? 0 : key;\\n var r = maybeGetEntry(this, key);\\n if (!r.list) {\\n r.list = (this.data_[r.id] = []);\\n }\\n if (!r.entry) {\\n r.entry = {\\n next: this.head_,\\n previous: this.head_.previous,\\n head: this.head_,\\n key: key,\\n value: value,\\n };\\n r.list.push(r.entry);\\n this.head_.previous.next = r.entry;\\n this.head_.previous = r.entry;\\n this.size++;\\n } else {\\n r.entry.value = value;\\n }\\n return this;\\n };\\n\\n\\n /** @override */\\n PolyfillMap.prototype.delete = function(key) {\\n var r = maybeGetEntry(this, key);\\n if (r.entry && r.list) {\\n r.list.splice(r.index, 1);\\n if (!r.list.length) delete this.data_[r.id];\\n r.entry.previous.next = r.entry.next;\\n r.entry.next.previous = r.entry.previous;\\n r.entry.head = null;\\n this.size--;\\n return true;\\n }\\n return false;\\n };\\n\\n\\n /** @override */\\n PolyfillMap.prototype.clear = function() {\\n this.data_ = {};\\n this.head_ = this.head_.previous = createHead();\\n this.size = 0;\\n };\\n\\n\\n /** @override */\\n PolyfillMap.prototype.has = function(key) {\\n return !!(maybeGetEntry(this, key).entry);\\n };\\n\\n\\n /** @override */\\n PolyfillMap.prototype.get = function(key) {\\n var entry = maybeGetEntry(this, key).entry;\\n // NOTE: this cast is a lie, but so is the extern.\\n return /** @type {VALUE} */ (entry &&\\n /** @type {VALUE} */ (entry.value));\\n };\\n\\n\\n /** @override */\\n PolyfillMap.prototype.entries = function() {\\n return makeIterator(this, /** @return {!Array<(KEY|VALUE)>} */ function(\\n /** !$jscomp.MapEntry */ entry) {\\n return ([entry.key, entry.value]);\\n });\\n };\\n\\n\\n /** @override */\\n PolyfillMap.prototype.keys = function() {\\n return makeIterator(this, /** @return {KEY} */ function(\\n /** !$jscomp.MapEntry */ entry) {\\n return entry.key;\\n });\\n };\\n\\n\\n /** @override */\\n PolyfillMap.prototype.values = function() {\\n return makeIterator(this, /** @return {VALUE} */ function(\\n /** !$jscomp.MapEntry */ entry) {\\n return entry.value;\\n });\\n };\\n\\n\\n /** @override */\\n PolyfillMap.prototype.forEach = function(callback, opt_thisArg) {\\n var iter = this.entries();\\n var item;\\n while (!(item = iter.next()).done) {\\n var entry = item.value;\\n callback.call(\\n /** @type {?} */ (opt_thisArg),\\n /** @type {VALUE} */ (entry[1]),\\n /** @type {KEY} */ (entry[0]),\\n this);\\n }\\n };\\n\\n\\n /** @type {?} */ (PolyfillMap.prototype)[Symbol.iterator] =\\n PolyfillMap.prototype.entries;\\n\\n\\n /**\\n * Returns an entry or undefined.\\n * @param {!PolyfillMap} map\\n * @param {KEY} key\\n * @return {{id: string,\\n * list: (!Array>|undefined),\\n * index: number,\\n * entry: (!$jscomp.MapEntry|undefined)}}\\n * @template KEY, VALUE\\n */\\n var maybeGetEntry = function(map, key) {\\n var id = getId(key);\\n var list = map.data_[id];\\n if (list && $jscomp.owns(map.data_, id)) {\\n for (var index = 0; index < list.length; index++) {\\n var entry = list[index];\\n if ((key !== key && entry.key !== entry.key) || key === entry.key) {\\n return {id: id, list: list, index: index, entry: entry};\\n }\\n }\\n }\\n return {id: id, list: list, index: -1, entry: undefined};\\n };\\n\\n\\n /**\\n * Maps over the entries with the given function.\\n * @param {!PolyfillMap} map\\n * @param {function(!$jscomp.MapEntry): T} func\\n * @return {!IteratorIterable}\\n * @template KEY, VALUE, T\\n * @private\\n */\\n var makeIterator = function(map, func) {\\n var entry = map.head_;\\n return $jscomp.iteratorPrototype(function() {\\n if (entry) {\\n while (entry.head != map.head_) {\\n entry = entry.previous;\\n }\\n while (entry.next != entry.head) {\\n entry = entry.next;\\n return {done: false, value: func(entry)};\\n }\\n entry = null; // make sure depletion is permanent\\n }\\n return {done: true, value: void 0};\\n });\\n };\\n\\n\\n /**\\n * Makes a new \\\"head\\\" element.\\n * @return {!$jscomp.Ma"; +a.a+="pEntry}\\n * @template KEY, VALUE\\n * @suppress {checkTypes} ignore missing key/value for head only\\n */\\n var createHead = function() {\\n var head = /** type {!$jscomp.MapEntry} */ ({});\\n head.previous = head.next = head.head = head;\\n return head;\\n };\\n\\n\\n /**\\n * Counter for generating IDs.\\n * @private {number}\\n */\\n var mapIndex = 0;\\n\\n\\n /**\\n * @param {*} obj An extensible object.\\n * @return {string} A unique ID.\\n */\\n var getId = function(obj) {\\n var type = obj && typeof obj;\\n if (type == 'object' || type == 'function') {\\n obj = /** @type {!Object} */ (obj);\\n if (!idMap.has(obj)) {\\n var id = '' + (++mapIndex);\\n idMap.set(obj, id);\\n return id;\\n }\\n return idMap.get(obj);\\n }\\n // Add a prefix since obj could be '__proto__';\\n return 'p_' + obj;\\n };\\n\\n\\n return PolyfillMap;\\n}, 'es6', 'es3');\\n\",\"js/es6/math.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Brings in all ES6 Math polyfills.\\n * @suppress {uselessCode}\\n */\\n'require es6/math/acosh';\\n'require es6/math/asinh';\\n'require es6/math/atanh';\\n'require es6/math/cbrt';\\n'require es6/math/clz32';\\n'require es6/math/cosh';\\n'require es6/math/expm1';\\n'require es6/math/fround';\\n'require es6/math/hypot';\\n'require es6/math/imul';\\n'require es6/math/log10';\\n'require es6/math/log1p';\\n'require es6/math/log2';\\n'require es6/math/sign';\\n'require es6/math/sinh';\\n'require es6/math/tanh';\\n'require es6/math/trunc';\\n\",\"js/es6/math/acosh.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Math.acosh', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Computes the inverse hyperbolic cosine.\\n *\\n *

      Polyfills the static function Math.acosh().\\n *\\n * @param {number} x Any number, or value that can be coerced to a number.\\n * @return {number} The inverse hyperbolic cosine of x.\\n */\\n var polyfill = function(x) {\\n x = Number(x);\\n return Math.log(x + Math.sqrt(x * x - 1));\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/math/asinh.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Math.asinh', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Computes the inverse hyperbolic sine.\\n *\\n *

      Polyfills the static function Math.asinh().\\n *\\n * @param {number} x Any number, or value that can be coerced to a number.\\n * @return {number} The inverse hyperbolic sine of x.\\n */\\n var polyfill = function(x) {\\n x = Number(x);\\n if (x === 0) return x;\\n var y = Math.log(Math.abs(x) + Math.sqrt(x * x + 1));\\n return x < 0 ? -y : y;\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/math/atanh.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n'require es6/math/log1p';\\n\\n$jscomp.polyfill('Math.atanh', function(orig) {\\n if (orig) return orig;\\n var log1p = Math.log1p;\\n\\n /**\\n * Computes the inverse hyperbolic tangent.\\n *\\n *

      Polyfills the static function Math.atanh().\\n *\\n * @param {number} x Any number, or value that can be coerced to a number.\\n * @return {number} The inverse hyperbolic tangent +x.\\n */\\n var polyfill = function(x) {\\n x = Number(x);\\n return (log1p(x) - log1p(-x)) / 2;\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/math/cbrt.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Math.cbrt', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Returns the cube root of the number, handling negatives safely.\\n *\\n *

      Polyfills the static function Math.cbrt().\\n *\\n * @param {number} x Any number, or value that can be coerced into a number.\\n * @return {number} The cube root of x.\\n */\\n var polyfill = function(x) {\\n if (x === 0) return x;\\n x = Number(x);\\n var y = Math.pow(Math.abs(x), 1 / 3);\\n return x < 0 ? -y : y;\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/math/clz32.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Math.clz32', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Counts the leading zeros in the 32-bit binary representation.\\n *\\n *

      Polyfills the static function Math.clz32().\\n *\\n * @param {number} x Any number, or value that can be coerced to a number.\\n * @return {number} The number of leading zero bits.\\n */\\n var polyfill = function(x) {\\n // This binary search algorithm is taken from v8.\\n x = Number(x) >>> 0; // first ensure we have a 32-bit unsigned integer.\\n if (x === 0) return 32;\\n var result = 0;\\n if ((x & 0xFFFF0000) === 0) {\\n x <<= 16;\\n result += 16;\\n }\\n if ((x & 0xFF000000) === 0) {\\n x <<= 8;\\n result += 8;\\n }\\n if ((x & 0xF0000000) === 0) {\\n x <<= 4;\\n result += 4;\\n }\\n if ((x & 0xC0000000) === 0) {\\n x <<= 2;\\n result += 2;\\n }\\n if ((x & 0x80000000) === 0) result++;\\n return result;\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/math/cosh.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Math.cosh', function(orig) {\\n if (orig) return orig;\\n var exp = Math.exp;\\n\\n /**\\n * Computes the hyperbolic cosine.\\n *\\n *

      Polyfills the static function Math.cosh().\\n *\\n * @param {number} x Any number, or value that can be coerced to a number.\\n * @return {number} The hyperbolic cosine of x.\\n */\\n var polyfill = function(x) {\\n x = Number(x);\\n return (exp(x) + exp(-x)) / 2;\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/math/expm1.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Implements Math.expm1 polyfill\\n * @suppress {uselessCode}\\n */\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Math.expm1', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Exponentiates x and then subtracts one. This is implemented in a\\n * way that is accurate for numbers close to zero.\\n *\\n *

      Polyfills the static function Math.expm1().\\n *\\n * @param {number} x Any number, or value that can be coerced to a number.\\n * @return {number} The exponential of x, less 1.\\n */\\n var polyfill = function(x) {\\n // This implementation is based on the Taylor expansion\\n // exp(x) ~ 1 + x + x^2/2 + x^3/6 + x^4/24 + ...\\n x = Number(x);\\n if (x < .25 && x > -.25) {\\n var y = x;\\n var d = 1;\\n var z = x;\\n var zPrev = 0;\\n while (zPrev != z) {\\n y *= x / (++d);\\n z = (zPrev = z) + y;\\n }\\n return z;\\n }\\n return Math.exp(x) - 1;\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/math/fround.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n'require util/defines';\\n\\n$jscomp.polyfill('Math.fround', function(orig) {\\n if (orig) return orig;\\n\\n if ($jscomp.SIMPLE_FROUND_POLYFILL || typeof Float32Array !== 'function') {\\n // Explicitly requested a no-op polyfill, or Float32Array not available.\\n return /** @return {number} */ function(/** number */ arg) {\\n return arg;\\n };\\n }\\n\\n var arr = new Float32Array(1);\\n /**\\n * Rounds the given double-precision number to single-precision (float32).\\n *\\n * Polyfills the static function Math.fround().\\n *\\n * This polyfill is slightly incorrect for IE8 and IE9, where it performs no\\n * rounding at all. This is generally not a problem, since Math.fround is\\n * primarily used for optimization (to force faster 32-bit operations rather\\n * than 64-bit), but in cases where (a) the logic actually depends on a\\n * correct fround implementation and (b) the application targets very old\\n * browsers, this polyfill will be insufficient. For that case, see\\n * https://gist.github.com/shicks/7a97ec6b3f10212e60a89a7f6d2d097d for a\\n * more correct polyfill that does not depend on Float32Array.\\n *\\n * @param {number} arg A 64-bit double-precision number.\\n * @return {number} The closest float32 to the argument.\\n */\\n var polyfill = function(arg) {\\n arr[0] = arg;\\n return arr[0];\\n };\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/math/hypot.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Math.hypot', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Returns the sum of its arguments in quadrature.\\n *\\n *

      Polyfills the static function Math.hypot().\\n *\\n * @param {...number} var_args Any number, or value that can be coerced to a\\n * number.\\n * @return {number} The square root of the sum of the squares.\\n */\\n var polyfill = function(var_args) {\\n if (arguments.length < 2) {\\n return arguments.length ? Math.abs(arguments[0]) : 0;\\n }\\n\\n var i, z, sum, max;\\n // Note: we need to normalize the numbers in case of over/underflow.\\n for (max = 0, i = 0; i < arguments.length; i++) {\\n max = Math.max(max, Math.abs(arguments[i]));\\n }\\n // TODO(sdh): Document where these constants come from.\\n if (max > 1e100 || max < 1e-100) {\\n if (!max) return max; // Handle 0 and NaN before trying to divide.\\n sum = 0;\\n for (i = 0; i < arguments.length; i++) {\\n z = Number(arguments[i]) / max;\\n sum += z * z;\\n }\\n return Math.sqrt(sum) * max;\\n } else {\\n sum = 0;\\n for (i = 0; i < arguments.length; i++) {\\n z = Number(arguments[i]);\\n sum += z * z;\\n }\\n return Math.sqrt(sum);\\n }\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/math/imul.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Math.imul', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Performs C-like 32-"; +a.a+="bit signed integer multiplication.\\n *\\n *

      Polyfills the static function Math.imul().\\n *\\n * @param {number} a Any number, or value that can be coerced to a number.\\n * @param {number} b Any number, or value that can be coerced to a number.\\n * @return {number} The 32-bit integer product of a and b.\\n */\\n var polyfill = function(a, b) {\\n // This algorithm is taken from v8.\\n // Note: If multiplication overflows 32 bits, then we risk losing\\n // precision. We must therefore break the inputs into 16-bit\\n // words and multiply separately.\\n a = Number(a);\\n b = Number(b);\\n var ah = (a >>> 16) & 0xFFFF; // Treat individual words as unsigned\\n var al = a & 0xFFFF;\\n var bh = (b >>> 16) & 0xFFFF;\\n var bl = b & 0xFFFF;\\n var lh = ((ah * bl + al * bh) << 16) >>> 0; // >>> 0 casts to uint\\n return (al * bl + lh) | 0; // | 0 casts back to signed\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/math/log10.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Math.log10', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Returns the base-10 logarithm.\\n *\\n *

      Polyfills the static function Math.log10().\\n *\\n * @param {number} x Any number, or value that can be coerced to a number.\\n * @return {number} The common log of x.\\n */\\n var polyfill = function(x) {\\n return Math.log(x) / Math.LN10;\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/math/log1p.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Math.log1p', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Returns the natural logarithm of 1+x, implemented in a way that is\\n * accurate for numbers close to zero.\\n *\\n *

      Polyfills the static function Math.log1p().\\n *\\n * @param {number} x Any number, or value that can be coerced to a number.\\n * @return {number} The natural log of 1+x.\\n */\\n var polyfill = function(x) {\\n // This implementation is based on the Taylor expansion\\n // log(1 + x) ~ x - x^2/2 + x^3/3 - x^4/4 + x^5/5 - ...\\n x = Number(x);\\n if (x < 0.25 && x > -0.25) {\\n var y = x;\\n var d = 1;\\n var z = x;\\n var zPrev = 0;\\n var s = 1;\\n while (zPrev != z) {\\n y *= x;\\n s *= -1;\\n z = (zPrev = z) + s * y / (++d);\\n }\\n return z;\\n }\\n return Math.log(1 + x);\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/math/log2.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Math.log2', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Returns the base-2 logarithm.\\n *\\n *

      Polyfills the static function Math.log2().\\n *\\n * @param {number} x Any number, or value that can be coerced to a number.\\n * @return {number} The base-2 log of x.\\n */\\n var polyfill = function(x) {\\n return Math.log(x) / Math.LN2;\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/math/sign.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Math.sign', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Returns the sign of the number, indicating whether it is\\n * positive, negative, or zero.\\n *\\n *

      Polyfills the static function Math.sign().\\n *\\n * @param {number} x Any number, or value that can be coerced to a number.\\n * @return {number} The sign, +1 if x is positive, -1 if x is\\n * negative, or 0 if x is zero.\\n */\\n var polyfill = function(x) {\\n x = Number(x);\\n return x === 0 || isNaN(x) ? x : x > 0 ? 1 : -1;\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/math/sinh.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Math.sinh', function(orig) {\\n if (orig) return orig;\\n var exp = Math.exp;\\n\\n /**\\n * Computes the hyperbolic sine.\\n *\\n *

      Polyfills the static function Math.sinh().\\n *\\n * @param {number} x Any number, or value that can be coerced to a number.\\n * @return {number} The hyperbolic sine of x.\\n */\\n var polyfill = function(x) {\\n x = Number(x);\\n if (x === 0) return x;\\n return (exp(x) - exp(-x)) / 2;\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/math/tanh.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Math.tanh', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Computes the hyperbolic tangent.\\n *\\n *

      Polyfills the static function Math.tanh().\\n *\\n * @param {number} x Any number, or value that can be coerced to a number.\\n * @return {number} The hyperbolic tangent of x.\\n */\\n var polyfill = function(x) {\\n x = Number(x);\\n if (x === 0) return x;\\n // Ensure exponent is negative to prevent overflow.\\n var y = Math.exp(-2 * Math.abs(x));\\n var z = (1 - y) / (1 + y);\\n return x < 0 ? -z : z;\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/math/trunc.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Math.trunc', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Truncates any fractional digits from its argument (towards zero).\\n *\\n *

      Polyfills the static function Math.trunc().\\n *\\n * @param {number} x Any number, or value that can be coerced to a number.\\n * @return {number}\\n */\\n var polyfill = function(x) {\\n x = Number(x);\\n if (isNaN(x) || x === Infinity || x === -Infinity || x === 0) return x;\\n var y = Math.floor(Math.abs(x));\\n return x < 0 ? -y : y;\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/modules/runtime.js\":\"/*\\n * Copyright 2018 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Light weight implementation of a module loader that is based on\\n * CommonJS.\\n *\\n * This is meant to be used by the Closure Library to help debug load transpiled\\n * ES6 modules. Closure can transpile ES6 modules to a function that is\\n * compatible with registerModule. Then it can call the global $jscomp.require\\n * when it wants to retrieve a reference to the module object.\\n *\\n * Example:\\n * \\\"import {x} from './other.js'; export {x as Y}; use(x);\\\"\\n *\\n * Might be transpiled as:\\n *\\n * $jscomp.registerModule(function($$exports, $$require, $$module) {\\n * Object.defineProperties($$exports, {\\n * Y: enumerable: true, get: function() { return module$other.x }\\n * });\\n * const module$other = $$require('./other.js');\\n * use(module$other.x);\\n * }, 'example.js', ['./other.js']);\\n *\\n * @suppress {uselessCode} The require statements below are not useless.\\n */\\n\\n'require base';\\n'require es6/map';\\n'require es6/set';\\n'require util/global';\\n\\n(function() {\\n/**\\n * @param {string} id\\n * @param {?=} opt_exports\\n *\\n * @struct @constructor @final\\n */\\nvar Module = function(id, opt_exports) {\\n /** @const {string} */\\n this.id = id;\\n /** @type {?} */\\n this.exports = opt_exports || {};\\n};\\n\\n\\n/**\\n * @param {?} other\\n */\\nModule.prototype.exportAllFrom = function(other) {\\n var module = this;\\n var define = {};\\n for (var key in other) {\\n if (key == 'default' || key in module.exports || key in define) {\\n continue;\\n }\\n define[key] = {\\n enumerable: true,\\n get: (function(key) {\\n return function() {\\n return other[key];\\n };\\n })(key)\\n };\\n }\\n $jscomp.global.Object.defineProperties(module.exports, define);\\n};\\n\\n\\n/**\\n * @param {?function(function(string), ?, !Module)} def The module definition\\n * function which has the arguments (require, exports, module).\\n * @param {!Module} module\\n * @param {string} path\\n *\\n * @struct @constructor @final\\n */\\nvar CacheEntry = function(def, module, path) {\\n /** @type {?function(function(string), ?, !Module)} */\\n this.def = def;\\n /** @type {!Module} */\\n this.module = module;\\n /** @type {string} */\\n this.path = path;\\n /** @const {!Set} */\\n this.blockingDeps = new Set();\\n};\\n\\n\\n/**\\n * Loads the module by calling its module definition function if it has not\\n * already been loaded.\\n *\\n * @return {?} The module's exports property.\\n */\\nCacheEntry.prototype.load = function() {\\n if (this.def) {\\n var def = this.def;\\n this.def = null;\\n callRequireCallback(def, this.module);\\n }\\n\\n return this.module.exports;\\n};\\n\\n\\n/**\\n * @param {function(function(string), ?, !Module)|function(function(string))}\\n * callback A module definition function with arguments (require, exports,\\n * module) or a require.ensure callback which has the argument (require).\\n * @param {!Module=} opt_module If provided then the callback is assumed to be\\n * this module's definition function.\\n */\\nfunction callRequireCallback(callback, opt_module) {\\n var oldPath = currentModulePath;\\n\\n try {\\n if (opt_module) {\\n currentModulePath = opt_module.id;\\n callback.call(\\n opt_module, createRequire(opt_module), opt_module.exports,\\n opt_module);\\n } else {\\n callback($jscomp.require);\\n }\\n } finally {\\n currentModulePath = oldPath;\\n }\\n}\\n\\n\\n/** @type {!Map} */\\nvar moduleCache = new Map();\\n\\n\\n/** @type {string} */\\nvar currentModulePath = '';\\n\\n\\n/**\\n * Normalize a file path by removing redundant \\\"..\\\" and extraneous \\\".\\\" file\\n * path components.\\n *\\n * @param {string} path\\n * @return {string}\\n */\\nfunction normalizePath(path) {\\n var components = path.split('/');\\n var i = 0;\\n while (i < components.length) {\\n if (components[i] == '.') {\\n components.splice(i, 1);\\n } else if (\\n i && components[i] == '..' && components[i - 1] &&\\n components[i - 1] != '..') {\\n components.splice(--i, 2);\\n } else {\\n i++;\\n }\\n }\\n return components.join('/');\\n}\\n\\n\\n/** @return {?string} */\\n$jscomp.getCurrentModulePath = function() {\\n return currentModulePath;\\n};\\n\\n\\n/**\\n * @param {string} id\\n * @return {!CacheEntry}\\n */\\nfunction getCacheEntry(id) {\\n var cacheEntry = moduleCache.get(id);\\n if (cacheEntry === undefined) {\\n throw new Error('Module ' + id + ' does not exist.');\\n }\\n return cacheEntry;\\n}\\n\\n\\n/**\\n * Map of absolute module path to list of require.ensure callbacks waiting for\\n * the given module to load.\\n *\\n * @const {!Map>}\\n */\\nvar ensureMap = new Map();\\n\\n\\n/**\\n * @param {!Set} requireSet\\n * @param {function(function(string))} callback\\n *\\n * @struct @constructor @final\\n */\\nvar CallbackEntry = function(requireSet, callback) {\\n /** @const */\\n this.requireSet = requireSet;\\n /** @const */\\n this.callback = callback;\\n};\\n\\n\\n/**\\n * Normalizes two paths if the second is relative.\\n *\\n * @param {string} root\\n * @param {string} absOrRelativePath\\n * @return {string}\\n */\\nfunction maybeNormalizePath(root, absOrRelativePath) {\\n if (absOrRelativePath.startsWith('./') ||\\n absOrRelativePath.startsWith('../')) {\\n return normalizePath(root + '/../' + absOrRelativePath);\\n } else {\\n return absOrRelativePath;\\n }\\n}\\n\\n\\n/**\\n * Creates a require function which resolves paths against the given module, if\\n * any.\\n *\\n * @param {!Module=} opt_module\\n * @return {function(string):?}\\n */\\nfunction createRequire(opt_module) {\\n /**\\n * @param {string} absOrRelativePath\\n * @return {?}\\n */\\n function require(absOrRelativePath) {\\n var absPath = absOrRelativePath;\\n if (opt_module) {\\n absPath = maybeNormalizePath(opt_module.id, absPath);\\n }\\n return getCacheEntry(absPath).load();\\n "; +a.a+="}\\n\\n /**\\n * @param {!Array} requires\\n * @param {function(function(string))} callback\\n */\\n function requireEnsure(requires, callback) {\\n if (currentModulePath) {\\n for (var i = 0; i < requires.length; i++) {\\n requires[i] = maybeNormalizePath(currentModulePath, requires[i]);\\n }\\n }\\n\\n var blockingRequires = [];\\n for (var i = 0; i < requires.length; i++) {\\n var required = moduleCache.get(requires[i]);\\n if (!required || required.blockingDeps.size) {\\n blockingRequires.push(requires[i]);\\n }\\n }\\n\\n if (blockingRequires.length) {\\n var requireSet = new Set(blockingRequires);\\n var callbackEntry = new CallbackEntry(requireSet, callback);\\n requireSet.forEach(function(require) {\\n var arr = ensureMap.get(require);\\n if (!arr) {\\n arr = [];\\n ensureMap.set(require, arr);\\n }\\n arr.push(callbackEntry);\\n });\\n } else {\\n callback(require);\\n }\\n }\\n require.ensure = requireEnsure;\\n\\n return require;\\n}\\n\\n\\n/** @const {function(string): ?} */\\n$jscomp.require = createRequire();\\n\\n\\n/**\\n * @param {string} id\\n * @return {boolean}\\n */\\n$jscomp.hasModule = function(id) {\\n return moduleCache.has(id);\\n};\\n\\n\\n/**\\n * Marks the given module as being available and calls any require.ensure\\n * callbacks waiting for it.\\n *\\n * @param {string} absModulePath\\n */\\nfunction markAvailable(absModulePath) {\\n var ensures = ensureMap.get(absModulePath);\\n\\n if (ensures) {\\n for (var i = 0; i < ensures.length; i++) {\\n var entry = ensures[i];\\n entry.requireSet.delete(absModulePath);\\n if (!entry.requireSet.size) {\\n ensures.splice(i--, 1);\\n callRequireCallback(entry.callback);\\n }\\n }\\n\\n if (!ensures.length) {\\n ensureMap.delete(absModulePath);\\n }\\n }\\n}\\n\\n\\n/**\\n * Registers a CommonJS-like module for use with this runtime. Does not execute\\n * the module until it is required.\\n *\\n * @param {function(function(string), ?, !Module)} moduleDef The module\\n * definition.\\n * @param {string} absModulePath\\n * @param {!Array=} opt_shallowDeps List of dependencies this module\\n * directly depends on. Paths can be relative to the given module. This\\n * module will considered available until all of its dependencies are also\\n * available for require.\\n */\\n$jscomp.registerModule = function(moduleDef, absModulePath, opt_shallowDeps) {\\n if (moduleCache.has(absModulePath)) {\\n throw new Error(\\n 'Module ' + absModulePath + ' has already been registered.');\\n }\\n\\n if (currentModulePath) {\\n throw new Error('Cannot nest modules.');\\n }\\n\\n var shallowDeps = opt_shallowDeps || [];\\n for (var i = 0; i < shallowDeps.length; i++) {\\n shallowDeps[i] = maybeNormalizePath(absModulePath, shallowDeps[i]);\\n }\\n\\n var /** !Set */ blockingDeps = new Set();\\n for (var i = 0; i < shallowDeps.length; i++) {\\n getTransitiveBlockingDepsOf(shallowDeps[i]).forEach(function(transitive) {\\n blockingDeps.add(transitive);\\n });\\n }\\n\\n // Make sure this module isn't blocking itself in the event of a cycle.\\n blockingDeps.delete(absModulePath);\\n\\n var cacheEntry =\\n new CacheEntry(moduleDef, new Module(absModulePath), absModulePath);\\n moduleCache.set(absModulePath, cacheEntry);\\n\\n blockingDeps.forEach(function(blocker) {\\n addAsBlocking(cacheEntry, blocker);\\n });\\n\\n if (!blockingDeps.size) {\\n markAvailable(cacheEntry.module.id);\\n }\\n\\n removeAsBlocking(cacheEntry);\\n};\\n\\n\\n/**\\n * @param {string} moduleId\\n * @return {!Set}\\n */\\nfunction getTransitiveBlockingDepsOf(moduleId) {\\n var cacheEntry = moduleCache.get(moduleId);\\n var /** !Set */ blocking = new Set();\\n\\n if (cacheEntry) {\\n cacheEntry.blockingDeps.forEach(function(dep) {\\n getTransitiveBlockingDepsOf(dep).forEach(function(transitive) {\\n blocking.add(transitive);\\n });\\n });\\n } else {\\n blocking.add(moduleId);\\n }\\n\\n return blocking;\\n}\\n\\n\\n/** @const {!Map>} */\\nvar blockingModulePathToBlockedModules = new Map();\\n\\n\\n/**\\n * @param {!CacheEntry} blocked\\n * @param {string} blocker\\n */\\nfunction addAsBlocking(blocked, blocker) {\\n if (blocked.module.id != blocker) {\\n var blockedModules = blockingModulePathToBlockedModules.get(blocker);\\n\\n if (!blockedModules) {\\n blockedModules = new Set();\\n blockingModulePathToBlockedModules.set(blocker, blockedModules);\\n }\\n\\n blockedModules.add(blocked);\\n blocked.blockingDeps.add(blocker);\\n }\\n}\\n\\n\\n/**\\n * Marks the given module as no longer blocking any modules. Instead marks the\\n * module's blockers as blocking these modules. If this totally unblocks a\\n * module it is marked as available.\\n *\\n * @param {!CacheEntry} cacheEntry\\n */\\nfunction removeAsBlocking(cacheEntry) {\\n var blocked = blockingModulePathToBlockedModules.get(cacheEntry.module.id);\\n\\n if (blocked) {\\n blockingModulePathToBlockedModules.delete(cacheEntry.module.id);\\n\\n blocked.forEach(function(blockedCacheEntry) {\\n blockedCacheEntry.blockingDeps.delete(cacheEntry.module.id);\\n\\n cacheEntry.blockingDeps.forEach(function(blocker) {\\n addAsBlocking(blockedCacheEntry, blocker);\\n });\\n\\n if (!blockedCacheEntry.blockingDeps.size) {\\n removeAsBlocking(blockedCacheEntry);\\n markAvailable(blockedCacheEntry.module.id);\\n }\\n });\\n }\\n}\\n\\n\\n/**\\n * Forces module evaluation as soon as it is available for require.\\n *\\n * @param {function(function(string), ?, !Module)} moduleDef\\n * @param {string} absModulePath\\n * @param {!Array} shallowDeps\\n * @suppress {strictMissingProperties} \\\"ensure\\\" is not declared.\\n */\\n$jscomp.registerAndLoadModule = function(\\n moduleDef, absModulePath, shallowDeps) {\\n $jscomp.require.ensure([absModulePath], function(require) {\\n require(absModulePath);\\n });\\n $jscomp.registerModule(moduleDef, absModulePath, shallowDeps);\\n};\\n\\n\\n/**\\n * Registers an object as if it is the exports of an ES6 module so that it may\\n * be retrieved via $jscomp.require.\\n *\\n * Used by Closure Library in the event that only some ES6 modules need\\n * transpilation.\\n *\\n * @param {string} absModulePath\\n * @param {?} exports\\n */\\n$jscomp.registerEs6ModuleExports = function(absModulePath, exports) {\\n if (moduleCache.has(absModulePath)) {\\n throw new Error(\\n 'Module at path ' + absModulePath + ' is already registered.');\\n }\\n\\n var entry =\\n new CacheEntry(null, new Module(absModulePath, exports), absModulePath);\\n moduleCache.set(absModulePath, entry);\\n markAvailable(absModulePath);\\n};\\n\\n\\n/**\\n * Hook to clear all loaded modules. Meant to only be used by tests.\\n */\\n$jscomp.clearModules = function() {\\n moduleCache.clear();\\n};\\n})();\\n\",\"js/es6/nopolyfill.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n\\n/**\\n * @fileoverview Specifies objects that the compiler does NOT polyfill.\\n * NOTE: this file should never be injected, since all the implementations\\n * are null.\\n */\\n\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Proxy', null, 'es6', 'es6');\\n\\n$jscomp.polyfill('String.raw', null, 'es6', 'es6');\\n$jscomp.polyfill('String.prototype.normalize', null, 'es6', 'es6');\\n\",\"js/es6/number.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Brings in all ES6 Number polyfills.\\n */\\n'require es6/number/constants';\\n'require es6/number/isfinite';\\n'require es6/number/isinteger';\\n'require es6/number/isnan';\\n'require es6/number/issafeinteger';\\n'require es6/number/parsefloat';\\n'require es6/number/parseint';\\n\",\"js/es6/number/constants.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n\\n$jscomp.polyfill('Number.EPSILON', function(orig) {\\n /**\\n * The difference 1 and the smallest number greater than 1.\\n *\\n *

      Polyfills the static field Number.EPSILON.\\n */\\n return Math.pow(2, -52);\\n}, 'es6', 'es3');\\n\\n\\n$jscomp.polyfill('Number.MAX_SAFE_INTEGER', function() {\\n /**\\n * The maximum safe integer, 2^53 - 1.\\n *\\n *

      Polyfills the static field Number.MAX_SAFE_INTEGER.\\n */\\n return 0x1fffffffffffff;\\n}, 'es6', 'es3');\\n\\n\\n$jscomp.polyfill('Number.MIN_SAFE_INTEGER', function() {\\n /**\\n * The minimum safe integer, -(2^53 - 1).\\n *\\n *

      Polyfills the static field Number.MIN_SAFE_INTEGER.\\n */\\n return -0x1fffffffffffff;\\n}, 'es6', 'es3');\\n\",\"js/es6/number/isfinite.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Number.isFinite', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Returns whether the given argument is a finite number.\\n *\\n *

      Polyfills the static function Number.isFinite().\\n *\\n * @param {number} x Any value.\\n * @return {boolean} True if x is a number and not NaN or infinite.\\n */\\n var polyfill = function(x) {\\n if (typeof x !== 'number') return false;\\n return !isNaN(x) && x !== Infinity && x !== -Infinity;\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/number/isinteger.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require es6/number/isfinite';\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Number.isInteger', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Returns whether the given argument is an integer.\\n *\\n *

      Polyfills the static function Number.isInteger().\\n *\\n * @param {number} x Any value.\\n * @return {boolean} True if x is an integer.\\n */\\n var polyfill = function(x) {\\n if (!Number.isFinite(x)) return false;\\n return x === Math.floor(x);\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/number/isnan.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Number.isNaN', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Returns whether the given argument is the value NaN,\\n * guaranteeing not to coerce to a number first.\\n *\\n *

      Polyfills the static function Number.isNaN().\\n *\\n * @param {number} x Any value.\\n * @return {boolean} True if x is exactly NaN.\\n */\\n var polyfill = function(x) {\\n return typeof x === 'number' && isNaN(x);\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/number/issafeinteger.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require es6/number/constants';\\n'require es6/number/isinteger';\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Number.isSafeInteger', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Returns whether the given argument is a \\\"safe\\\" integer,\\n * that is, its magnitude is less than 2^53.\\n *\\n *

      Polyfills the static function Number.isSafeInteger().\\n *\\n * @param {number} x Any value.\\n * @return {boolean} True if x is a safe integer.\\n */\\n var polyfill = function(x) {\\n return Number.isInteger(x) && Math.abs(x) <= Number.MAX_SAFE_INTEGER;\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/number/parsefloat.js\":\"/*\\n * Copyright 2017 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Number.parseFloat', function(orig) {\\n return orig || parseFloat;\\n}, 'es6', 'es3');\\n\",\"js/es6/number/parseint.js\":\"/*\\n * Copyright 2017 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distri"; +a.a+="buted under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Number.parseInt', function(orig) {\\n return orig || parseInt;\\n}, 'es6', 'es3');\\n\",\"js/es6/object.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Brings in all ES6 Object polyfills.\\n */\\n'require es6/object/assign';\\n'require es6/object/entries';\\n'require es6/object/getownpropertydescriptors';\\n'require es6/object/getownpropertysymbols';\\n'require es6/object/is';\\n'require es6/object/setprototypeof';\\n'require es6/object/values';\\n\",\"js/es6/object/assign.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview\\n * @suppress {uselessCode}\\n */\\n'require es6/util/assign';\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Object.assign', function(orig) {\\n return orig || $jscomp.assign;\\n}, 'es6', 'es3');\\n\",\"js/es6/object/entries.js\":\"/*\\n * Copyright 2017 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/owns';\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Object.entries', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Returns an array of [key, value] arrays, one for each entry\\n * in the given object.\\n *\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries\\n *\\n * @param {!Object} obj\\n * @return {!Array>}\\n * @template KEY, VALUE\\n */\\n var entries = function(obj) {\\n var result = [];\\n for (var key in obj) {\\n if ($jscomp.owns(obj, key)) {\\n result.push([key, obj[key]]);\\n }\\n }\\n return result;\\n };\\n\\n return entries;\\n}, 'es8', 'es3');\\n\",\"js/es6/object/getownpropertydescriptors.js\":\"/*\\n * Copyright 2017 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require es6/reflect/ownkeys';\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Object.getOwnPropertyDescriptors', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Polyfills Object.getOwnPropertyDescriptors.\\n *\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries\\n *\\n * @param {!Object} obj\\n * @return {!Object}\\n */\\n var getOwnPropertyDescriptors = function(obj) {\\n var result = {};\\n var keys = Reflect.ownKeys(obj);\\n for (var i = 0; i < keys.length; i++) {\\n result[keys[i]] = Object.getOwnPropertyDescriptor(obj, keys[i]);\\n }\\n return result;\\n };\\n\\n return getOwnPropertyDescriptors;\\n}, 'es8', 'es5');\\n\",\"js/es6/object/getownpropertysymbols.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Object.getOwnPropertySymbols', function(orig) {\\n if (orig) return orig;\\n\\n // NOTE: The symbol polyfill is a string, so symbols show up in\\n // Object.getOwnProperytyNames instead. It's been decided that\\n // the trade-off of \\\"fixing\\\" this behavior is not worth the costs\\n // in (a) code size, (b) brittleness, and (c) complexity.\\n return function() { return []; };\\n}, 'es6', 'es5'); // Same as Object.getOwnPropertyNames\\n\",\"js/es6/object/is.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Object.is', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Polyfill for Object.is() method:\\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is\\n *\\n * Determines whether two values are the same value (that is,\\n * functionally equivalent). This is the same as ===-equality,\\n * except for two cases: 0 is not the same as -0, and NaN is\\n * the same as NaN.\\n *\\n * @param {*} left\\n * @param {*} right\\n * @return {boolean}\\n */\\n var polyfill = function(left, right) {\\n if (left === right) {\\n // Handle the 0 === -0 exception\\n return (left !== 0) || (1 / left === 1 / /** @type {number} */ (right));\\n } else {\\n // Handle the NaN !== NaN exception\\n return (left !== left) && (right !== right);\\n }\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/object/setprototypeof.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview\\n * @suppress {uselessCode}\\n */\\n'require util/polyfill';\\n'require es6/util/setprototypeof';\\n\\n\\n$jscomp.polyfill('Object.setPrototypeOf', function(orig) {\\n // Note that $jscomp.setPrototypeOf will be `null` if it isn't possible to\\n // implement this method.\\n return orig || $jscomp.setPrototypeOf;\\n}, 'es6', 'es5');\\n\",\"js/es6/object/values.js\":\"/*\\n * Copyright 2017 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/owns';\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Object.values', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Returns an array of values from the given object.\\n *\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values\\n *\\n * @param {!Object} obj\\n * @return {!Array}\\n * @template KEY, VALUE\\n */\\n var values = function(obj) {\\n var result = [];\\n for (var key in obj) {\\n if ($jscomp.owns(obj, key)) {\\n result.push(obj[key]);\\n }\\n }\\n return result;\\n };\\n\\n return values;\\n}, 'es8', 'es3');\\n\",\"js/es6/promise.js\":\"/*\\n * Copyright 2018 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Brings in all Promise polyfills.\\n */\\n'require es6/promise/promise';\\n'require es6/promise/finally';\\n\",\"js/es6/promise/finally.js\":\"/*\\n * Copyright 2018 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n'require es6/promise/promise';\\n\\n$jscomp.polyfill('Promise.prototype.finally', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * @this {!Promise}\\n * @param {function():?} onFinally\\n * @return {!Promise}\\n * @template VALUE\\n * @suppress {reportUnknownTypes}\\n */\\n var polyfill = function(onFinally) {\\n return this.then(\\n function(value) {\\n var promise = Promise.resolve(onFinally());\\n return promise.then(function () { return value; });\\n },\\n function(reason) {\\n var promise = Promise.resolve(onFinally());\\n return promise.then(function () { throw reason; });\\n });\\n };\\n\\n return polyfill;\\n}, 'es9', 'es3');\\n\",\"js/es6/promise/promise.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require base';\\n'require es6/util/makeiterator';\\n'require util/global';\\n'require util/polyfill';\\n\\n/**\\n * Should we unconditionally override a native Promise implementation with our\\n * own?\\n * @define {boolean}\\n */\\n$jscomp.FORCE_POLYFILL_PROMISE = false;\\n\\n\\n$jscomp.polyfill('Promise',\\n /**\\n * @param {*} NativePromise\\n * @return {*}\\n * @suppress {reportUnknownTypes}\\n */\\n function(NativePromise) {\\n // TODO(bradfordcsmith): Do we need to add checks for standards conformance?\\n // e.g. The version of FireFox we currently use for testing has a Promise\\n // that fails to reject attempts to fulfill it with itself, but that\\n // isn't reasonably testable here.\\n if (NativePromise && !$jscomp.FORCE_POLYFILL_PROMISE) {\\n return NativePromise;\\n }\\n\\n /**\\n * Schedules code to be executed asynchronously.\\n * @constructor\\n * @struct\\n */\\n function AsyncExecutor() {\\n /**\\n * Batch of functions to execute.\\n *\\n * Will be `null` initially and immediately after a batch finishes\\n * executing.\\n * @private {?Array}\\n */\\n this.batch_ = null;\\n }\\n\\n /**\\n * Schedule a function to execute asynchronously.\\n *\\n * - The function will execute:\\n * - After the current call stack has completed executing.\\n * - After any functions previously scheduled using this object.\\n * - The return value will be ignored.\\n * - An exception thrown by the method will be caught and asynchronously\\n * rethrown when it cannot interrupt any other code. This class provides\\n * no way to catch such exceptions.\\n * @param {function():?} f\\n */\\n AsyncExecutor.prototype.asyncExecute = function(f) {\\n if (this.batch_ == null) {\\n // no batch created yet, or last batch was fully executed\\n this.batch_ = [];\\n var self = this;\\n this.asyncExecuteFunction(function() { self.executeBatch_(); });\\n }\\n this.batch_.push(f);\\n };\\n\\n // NOTE: We want to make sure AsyncExecutor will work as expected even if\\n // testing code should override setTimeout()\\n /** @const {function(!Function, number)} */\\n var nativeSetTimeout = $jscomp.global['setTimeout'];\\n\\n /**\\n * Schedule a function to execute asynchronously as soon as possible.\\n *\\n * NOTE: May be overridden for testing.\\n * @package\\n * @param {function()} f\\n */\\n AsyncExecutor.prototype.asyncExecuteFunction = function(f) {\\n nativeSetTimeout(f, 0);\\n };\\n\\n /**\\n * Execute scheduled jobs in a batch until all are executed or the batch\\n * execution time limit has been reached.\\n * @private\\n */\\n AsyncExecutor.prototype.executeBatch_ = function() {\\n while (this.batch_ && this.batch_.length) {\\n var /** !Array */ executingBatch = this.batch_;\\n // Executions scheduled while executing this batch go into a new one to\\n // avoid the batch array getting too big.\\n this.batch_ = [];\\n for (var i = 0; i < executingBatch.length; ++i) {\\n var f = /** @type {function()} */ (executingBatch[i]);\\n executingBatch[i] = null; // free memory\\n try {\\n f();\\n } catch (error) {\\n this.asyncThrow_(error);\\n }\\n }\\n }\\n // All jobs finished executing, so force scheduling a new batch next\\n // time asyncExecute() is called.\\n this.batch_ = null;\\n };\\n\\n /*"; +a.a+="*\\n * @private\\n * @param {*} exception\\n */\\n AsyncExecutor.prototype.asyncThrow_ = function(exception) {\\n this.asyncExecuteFunction(function() { throw exception; });\\n };\\n\\n /**\\n * @enum {number}\\n */\\n var PromiseState = {\\n /** The Promise is waiting for resolution. */\\n PENDING: 0,\\n\\n /** The Promise has been resolved with a fulfillment value. */\\n FULFILLED: 1,\\n\\n /** The Promise has been resolved with a rejection reason. */\\n REJECTED: 2\\n };\\n\\n\\n /**\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise\\n * @param {function(\\n * function((TYPE|IThenable|Thenable|null)=),\\n * function(*=))} executor\\n * @constructor\\n * @extends {Promise}\\n * @template TYPE\\n */\\n var PolyfillPromise = function(executor) {\\n /** @private {PromiseState} */\\n this.state_ = PromiseState.PENDING;\\n\\n /**\\n * The settled result of the Promise. Immutable once set with either a\\n * fulfillment value or rejection reason.\\n * @private {*}\\n */\\n this.result_ = undefined;\\n\\n /**\\n * These functions must be asynchronously executed when this promise\\n * settles.\\n * @private {?Array}\\n */\\n this.onSettledCallbacks_ = [];\\n\\n var resolveAndReject = this.createResolveAndReject_();\\n try {\\n executor(resolveAndReject.resolve, resolveAndReject.reject);\\n } catch (e) {\\n resolveAndReject.reject(e);\\n }\\n };\\n\\n\\n /**\\n * Create a pair of functions for resolving or rejecting this Promise.\\n *\\n *

      After the resolve or reject function has been called once, later calls\\n * do nothing.\\n * @private\\n * @return {{\\n * resolve: function((TYPE|IThenable|Thenable|null)=),\\n * reject: function(*=)\\n * }}\\n */\\n PolyfillPromise.prototype.createResolveAndReject_ = function() {\\n var thisPromise = this;\\n var alreadyCalled = false;\\n /**\\n * @param {function(this:PolyfillPromise, T)} method\\n * @return {function(T)}\\n * @template T\\n */\\n function firstCallWins(method) {\\n return function(x) {\\n if (!alreadyCalled) {\\n alreadyCalled = true;\\n method.call(thisPromise, x);\\n }\\n };\\n }\\n return {\\n resolve: firstCallWins(this.resolveTo_),\\n reject: firstCallWins(this.reject_)\\n };\\n };\\n\\n\\n /**\\n * @private\\n * @param {*} value\\n */\\n PolyfillPromise.prototype.resolveTo_ = function(value) {\\n if (value === this) {\\n this.reject_(new TypeError('A Promise cannot resolve to itself'));\\n } else if (value instanceof PolyfillPromise) {\\n this.settleSameAsPromise_(/** @type {!PolyfillPromise} */ (value));\\n } else if (isObject(value)) {\\n this.resolveToNonPromiseObj_(/** @type {!Object} */ (value));\\n } else {\\n this.fulfill_(value);\\n }\\n };\\n\\n\\n /**\\n * @private\\n * @param {!Object} obj\\n * @suppress {strictMissingProperties} obj.then\\n */\\n PolyfillPromise.prototype.resolveToNonPromiseObj_ = function(obj) {\\n var thenMethod = undefined;\\n\\n try {\\n thenMethod = obj.then;\\n } catch (error) {\\n this.reject_(error);\\n return;\\n }\\n if (typeof thenMethod == 'function') {\\n this.settleSameAsThenable_(thenMethod, /** @type {!Thenable} */ (obj));\\n } else {\\n this.fulfill_(obj);\\n }\\n };\\n\\n\\n /**\\n * @param {*} value anything\\n * @return {boolean}\\n */\\n function isObject(value) {\\n switch (typeof value) {\\n case 'object':\\n return value != null;\\n case 'function':\\n return true;\\n default:\\n return false;\\n }\\n }\\n\\n /**\\n * Reject this promise for the given reason.\\n * @private\\n * @param {*} reason\\n * @throws {!Error} if this promise is already fulfilled or rejected.\\n */\\n PolyfillPromise.prototype.reject_ = function(reason) {\\n this.settle_(PromiseState.REJECTED, reason);\\n };\\n\\n /**\\n * Fulfill this promise with the given value.\\n * @private\\n * @param {!TYPE} value\\n * @throws {!Error} when this promise is already fulfilled or rejected.\\n */\\n PolyfillPromise.prototype.fulfill_ = function(value) {\\n this.settle_(PromiseState.FULFILLED, value);\\n };\\n\\n /**\\n * Fulfill or reject this promise with the given value/reason.\\n * @private\\n * @param {!PromiseState} settledState (FULFILLED or REJECTED)\\n * @param {*} valueOrReason\\n * @throws {!Error} when this promise is already fulfilled or rejected.\\n */\\n PolyfillPromise.prototype.settle_ = function(settledState, valueOrReason) {\\n if (this.state_ != PromiseState.PENDING) {\\n throw new Error(\\n 'Cannot settle(' + settledState + ', ' + valueOrReason +\\n '): Promise already settled in state' + this.state_);\\n }\\n this.state_ = settledState;\\n this.result_ = valueOrReason;\\n this.executeOnSettledCallbacks_();\\n };\\n\\n PolyfillPromise.prototype.executeOnSettledCallbacks_ = function() {\\n if (this.onSettledCallbacks_ != null) {\\n for (var i = 0; i < this.onSettledCallbacks_.length; ++i) {\\n asyncExecutor.asyncExecute(this.onSettledCallbacks_[i]);\\n }\\n this.onSettledCallbacks_ = null; // free memory\\n }\\n };\\n\\n /**\\n * All promise async execution is managed by a single executor for the\\n * sake of efficiency.\\n * @const {!AsyncExecutor}\\n */\\n var asyncExecutor = new AsyncExecutor();\\n\\n /**\\n * Arrange to settle this promise in the same way as the given thenable.\\n * @private\\n * @param {!PolyfillPromise} promise\\n */\\n PolyfillPromise.prototype.settleSameAsPromise_ = function(promise) {\\n var methods = this.createResolveAndReject_();\\n\\n // Calling then() would create an unnecessary extra promise.\\n promise.callWhenSettled_(methods.resolve, methods.reject);\\n };\\n\\n /**\\n * Arrange to settle this promise in the same way as the given thenable.\\n * @private\\n * @param {function(\\n * function((TYPE|IThenable|Thenable|null)=),\\n * function(*=))\\n * } thenMethod\\n * @param {!Thenable} thenable\\n */\\n PolyfillPromise.prototype.settleSameAsThenable_ = function(\\n thenMethod, thenable) {\\n var methods = this.createResolveAndReject_();\\n\\n // Don't trust an unknown thenable implementation not to throw exceptions.\\n try {\\n thenMethod.call(thenable, methods.resolve, methods.reject);\\n } catch (error) {\\n methods.reject(error);\\n }\\n };\\n\\n /** @override */\\n PolyfillPromise.prototype.then = function(onFulfilled, onRejected) {\\n var resolveChild;\\n var rejectChild;\\n var childPromise = new PolyfillPromise(function(resolve, reject) {\\n resolveChild = resolve;\\n rejectChild = reject;\\n });\\n function createCallback(paramF, defaultF) {\\n // The spec says to ignore non-function values for onFulfilled and\\n // onRejected\\n if (typeof paramF == 'function') {\\n return function(x) {\\n try {\\n resolveChild(paramF(x));\\n } catch (error) {\\n rejectChild(error);\\n }\\n };\\n } else {\\n return defaultF;\\n }\\n }\\n\\n this.callWhenSettled_(\\n createCallback(onFulfilled, resolveChild),\\n createCallback(onRejected, rejectChild));\\n return childPromise;\\n };\\n\\n /** @override */\\n PolyfillPromise.prototype.catch = function(onRejected) {\\n return this.then(undefined, onRejected);\\n };\\n\\n\\n PolyfillPromise.prototype.callWhenSettled_ = function(\\n onFulfilled, onRejected) {\\n var /** !PolyfillPromise */ thisPromise = this;\\n function callback() {\\n switch (thisPromise.state_) {\\n case PromiseState.FULFILLED:\\n onFulfilled(thisPromise.result_);\\n break;\\n case PromiseState.REJECTED:\\n onRejected(thisPromise.result_);\\n break;\\n default:\\n throw new Error('Unexpected state: ' + thisPromise.state_);\\n }\\n }\\n if (this.onSettledCallbacks_ == null) {\\n // we've already settled\\n asyncExecutor.asyncExecute(callback);\\n } else {\\n this.onSettledCallbacks_.push(callback);\\n }\\n };\\n\\n // called locally, so give it a name\\n function resolvingPromise(opt_value) {\\n if (opt_value instanceof PolyfillPromise) {\\n return opt_value;\\n } else {\\n return new PolyfillPromise(function(resolve, reject) {\\n resolve(opt_value);\\n });\\n }\\n }\\n PolyfillPromise['resolve'] = resolvingPromise;\\n\\n\\n PolyfillPromise['reject'] = function(opt_reason) {\\n return new PolyfillPromise(function(resolve, reject) {\\n reject(opt_reason);\\n });\\n };\\n\\n\\n PolyfillPromise['race'] = function(thenablesOrValues) {\\n return new PolyfillPromise(function(resolve, reject) {\\n var /** !Iterator<*> */ iterator =\\n $jscomp.makeIterator(thenablesOrValues);\\n for (var /** !IIterableResult<*> */ iterRec = iterator.next();\\n !iterRec.done;\\n iterRec = iterator.next()) {\\n // Using resolvingPromise() allows us to treat all elements the same\\n // way.\\n // NOTE: resolvingPromise(promise) always returns the argument\\n // unchanged.\\n // Using .callWhenSettled_() instead of .then() avoids creating an\\n // unnecessary extra promise.\\n resolvingPromise(iterRec.value).callWhenSettled_(resolve, reject);\\n }\\n });\\n };\\n\\n\\n PolyfillPromise['all'] = function(thenablesOrValues) {\\n var /** !Iterator<*> */ iterator = $jscomp.makeIterator(thenablesOrValues);\\n var /** !IIterableResult<*> */ iterRec = iterator.next();\\n\\n if (iterRec.done) {\\n return resolvingPromise([]);\\n } else {\\n return new PolyfillPromise(function(resolveAll, rejectAll) {\\n var resultsArray = [];\\n var unresolvedCount = 0;\\n\\n function onFulfilled(i) {\\n return function(ithResult) {\\n resultsArray[i] = ithResult;\\n unresolvedCount--;\\n if (unresolvedCount == 0) {\\n resolveAll(resultsArray);\\n }\\n };\\n }\\n\\n do {\\n resultsArray.push(undefined);\\n unresolvedCount++;\\n // Using resolvingPromise() allows us to treat all elements the same\\n // way.\\n // NOTE: resolvingPromise(promise) always returns the argument\\n // unchanged. Using .callWhenSettled_() instead of .then() avoids\\n // creating an unnecessary extra promise.\\n resolvingPromise(iterRec.value)\\n .callWhenSettled_(\\n onFulfilled(resultsArray.length - 1), rejectAll);\\n iterRec = iterator.next();\\n } while (!iterRec.done);\\n });\\n }\\n };\\n\\n return PolyfillPromise;\\n}, 'es6', 'es3');\\n\",\"js/es6/reflect.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Rolls together all ES6 Reflect polyfills.\\n */\\n'require es6/reflect/apply';\\n'require es6/reflect/construct';\\n'require es6/reflect/defineproperty';\\n'require es6/reflect/deleteproperty';\\n'require es6/reflect/get';\\n'require es6/reflect/getownpropertydescriptor';\\n'require es6/reflect/getprototypeof';\\n'require es6/reflect/has';\\n'require es6/reflect/isextensible';\\n'require es6/reflect/ownkeys';\\n'require es6/reflect/preventextensions';\\n'require es6/reflect/set';\\n'require es6/reflect/setprototypeof';\\n\",\"js/es6/reflect/apply.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n\\n$jscomp.polyfill('Reflect.apply', function(orig) {\\n if (orig) return orig;\\n var apply = Function.prototype.apply;\\n\\n /**\\n * Polyfill for Reflect.apply() method:\\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/apply\\n *\\n * Calls a target function with arguments as specified, just\\n * as Function.prototype.apply.\\n *\\n * @param {function(this: THIS, ...*): RESULT} target The function to call.\\n * @param {THIS} thisArg The 'this' argument.\\n * @param {!Array} argList The arguments as a list.\\n * @return {RESULT} The result of the function call.\\n * @template THIS, RESULT\\n * @suppress {reportUnknownTypes}\\n */\\n var polyfill = function(target, thisArg, argList) {\\n return apply.call(target, thisArg, argList);\\n };\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/reflect/construct.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview\\n * @suppress {uselessCode}\\n */\\n\\n'require es6/util/construct';\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Reflect.construct', function(orig) {\\n // NOTE: This is just Reflect.construct if it's present and functional.\\n return $jscomp.construct;\\n}, 'es6', 'es3');\\n\",\"js/es6/reflect/defineproperty.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n\\n$jscomp.polyfill('Reflect.defineProperty', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Polyfill for Reflect.defineProperty() method:\\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/defineProperty\\n *\\n * Version of Object.defineProperty that returns a boolean.\\n *\\n * @param {!Object} target Target on which to define the property.\\n * @param {string} propertyKey Name of the property to define.\\n * @param {!ObjectPropertyDescriptor} attributes Property attributes.\\n * @return {boolean} Whether the property was defined.\\n */\\n var polyfill = function(target, propertyKey, attributes) {\\n try {\\n Object.defineProperty(target, propertyKey, attributes);\\n var desc = Object.getOwnPropertyDescriptor(target, propertyKey);\\n if (!desc) return false;\\n return desc.configurable === (attributes.configurable || false) &&\\n desc.enumerable === (attributes.enumerable || false) &&\\n ('value' in desc ?\\n desc.value === attributes.value &&\\n des"; +a.a+="c.writable === (attributes.writable || false) :\\n desc.get === attributes.get &&\\n desc.set === attributes.set);\\n } catch (err) {\\n return false;\\n }\\n };\\n return polyfill;\\n}, 'es6', 'es5'); // ES5: Requires Object.defineProperty\\n\",\"js/es6/reflect/deleteproperty.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/owns';\\n'require util/polyfill';\\n\\n\\n$jscomp.polyfill('Reflect.deleteProperty', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Polyfill for Reflect.deleteProperty() method:\\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/deleteProperty\\n *\\n * Applies the 'delete' operator as a function.\\n *\\n * @param {!Object} target Target on which to delete the property.\\n * @param {string} propertyKey Name of the property to delete.\\n * @return {boolean} Whether the property was deleted.\\n */\\n var polyfill = function(target, propertyKey) {\\n if (!$jscomp.owns(target, propertyKey)) {\\n return true;\\n }\\n try {\\n return delete target[propertyKey];\\n } catch (err) {\\n return false;\\n }\\n };\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/reflect/get.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/finddescriptor';\\n'require util/polyfill';\\n\\n\\n$jscomp.polyfill('Reflect.get', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Polyfill for Reflect.get() method:\\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/get\\n *\\n * Applies the 'getprop' operator as a function.\\n *\\n * @param {!Object} target Target on which to get the property.\\n * @param {string} propertyKey Name of the property to get.\\n * @param {!Object=} opt_receiver An optional 'this' to use for a getter.\\n * @return {*} The value of the property.\\n * @suppress {reportUnknownTypes}\\n */\\n var polyfill = function(target, propertyKey, opt_receiver) {\\n if (arguments.length <= 2) {\\n return target[propertyKey];\\n }\\n var property = $jscomp.findDescriptor(target, propertyKey);\\n if (property) {\\n return property.get ? property.get.call(opt_receiver) : property.value;\\n }\\n return undefined;\\n };\\n return polyfill;\\n}, 'es6', 'es5'); // ES5: findDescriptor requires getPrototypeOf\\n\",\"js/es6/reflect/getownpropertydescriptor.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n\\n$jscomp.polyfill('Reflect.getOwnPropertyDescriptor', function(orig) {\\n // NOTE: We don't make guarantees about correct throwing behavior.\\n // Non-object arguments should be prevented by the type checker.\\n return orig || Object.getOwnPropertyDescriptor;\\n}, 'es6', 'es5'); // ES5: Requires Object.getOwnPropertyDescriptor\\n\",\"js/es6/reflect/getprototypeof.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n\\n$jscomp.polyfill('Reflect.getPrototypeOf', function(orig) {\\n // NOTE: We don't make guarantees about correct throwing behavior.\\n // Non-object arguments should be prevented by the type checker.\\n return orig || Object.getPrototypeOf;\\n}, 'es6', 'es5'); // ES5: Requires Object.getPrototypeOf\\n\",\"js/es6/reflect/has.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n\\n$jscomp.polyfill('Reflect.has', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Polyfill for Reflect.has() method:\\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/has\\n *\\n * Applies the 'in' operator as a function.\\n *\\n * @param {!Object} target\\n * @param {*} propertyKey\\n * @return {boolean}\\n */\\n var polyfill = function(target, propertyKey) {\\n return propertyKey in target;\\n };\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/reflect/isextensible.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/defines';\\n'require util/polyfill';\\n\\n\\n$jscomp.polyfill('Reflect.isExtensible', function(orig) {\\n if (orig) return orig;\\n if ($jscomp.ASSUME_ES5 || typeof Object.isExtensible == 'function') {\\n return Object.isExtensible;\\n }\\n return function() { return true; };\\n}, 'es6', 'es3');\\n\",\"js/es6/reflect/ownkeys.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n'require es6/object/getownpropertysymbols';\\n\\n\\n$jscomp.polyfill('Reflect.ownKeys',\\n /**\\n * @param {*} orig\\n * @return {*}\\n * @suppress {reportUnknownTypes}\\n */\\n function(orig) {\\n if (orig) return orig;\\n\\n var symbolPrefix = 'jscomp_symbol_';\\n function isSymbol(key) {\\n return key.substring(0, symbolPrefix.length) == symbolPrefix;\\n }\\n\\n /**\\n * Polyfill for Reflect.ownKeys() method:\\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/ownKeys\\n *\\n * Returns an array of the object's own property keys.\\n *\\n * @param {!Object} target\\n * @return {!Array<(string|symbol)>}\\n */\\n var polyfill = function(target) {\\n var keys = [];\\n var names = Object.getOwnPropertyNames(target);\\n var symbols = Object.getOwnPropertySymbols(target);\\n for (var i = 0; i < names.length; i++) {\\n (isSymbol(names[i]) ? symbols : keys).push(names[i]);\\n }\\n return keys.concat(symbols);\\n };\\n return polyfill;\\n}, 'es6', 'es5'); // ES5: Requires Object.getOwnPropertyNames\\n\",\"js/es6/reflect/preventextensions.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/defines';\\n'require util/polyfill';\\n\\n\\n$jscomp.polyfill('Reflect.preventExtensions', function(orig) {\\n if (orig) return orig;\\n\\n if (!($jscomp.ASSUME_ES5 || typeof Object.preventExtensions == 'function')) {\\n return function() { return false; };\\n }\\n\\n /**\\n * Polyfill for Reflect.preventExtensions() method:\\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/preventExtensions\\n *\\n * Same function as Object.preventExtensions (the spec says\\n * to throw if the input is not an object, but jscompiler will\\n * fail to typecheck, so there's no reason to distinguish here).\\n *\\n * @param {!Object} target\\n * @return {boolean}\\n */\\n var polyfill = function(target) {\\n Object.preventExtensions(target);\\n return !Object.isExtensible(target);\\n };\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/reflect/set.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require es6/reflect/isextensible';\\n'require util/finddescriptor';\\n'require util/polyfill';\\n\\n\\n$jscomp.polyfill('Reflect.set', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Polyfill for Reflect.set() method:\\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/set\\n *\\n * Applies the 'setprop' operator as a function.\\n *\\n * @param {!Object} target Target on which to get the property.\\n * @param {string} propertyKey Name of the property to get.\\n * @param {*} value Value to set.\\n * @param {!Object=} opt_receiver An optional 'this' to use for a setter.\\n * @return {boolean} Whether setting was successful.\\n */\\n var polyfill = function(target, propertyKey, value, opt_receiver) {\\n var property = $jscomp.findDescriptor(target, propertyKey);\\n if (!property) {\\n if (Reflect.isExtensible(target)) {\\n target[propertyKey] = value;\\n return true;\\n }\\n return false;\\n }\\n if (property.set) {\\n property.set.call(arguments.length > 3 ? opt_receiver : target, value);\\n return true;\\n } else if (property.writable && !Object.isFrozen(target)) {\\n target[propertyKey] = value;\\n return true;\\n }\\n return false;\\n };\\n return polyfill;\\n}, 'es6', 'es5'); // ES5: findDescriptor requires getPrototypeOf\\n\",\"js/es6/reflect/setprototypeof.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview\\n * @suppress {uselessCode}\\n */\\n'require util/polyfill';\\n'require es6/util/setprototypeof';\\n\\n\\n$jscomp.polyfill(\\n 'Reflect.setPrototypeOf',\\n /**\\n * These annotations are intended to match the signature of\\n * $jscomp.polyfill(). Being more specific makes the compiler unhappy.\\n * @suppress {reportUnknownTypes}\\n * @param {?*} orig\\n * @return {*}\\n */\\n function(orig) {\\n if (orig) {\\n return orig;\\n } else if ($jscomp.setPrototypeOf) {\\n /** @const {!function(!Object,?Object):!Object} */\\n var setPrototypeOf = $jscomp.setPrototypeOf;\\n /**\\n * @param {!Object} target\\n * @param {?Object} proto\\n * @return {boolean}\\n */\\n var polyfill = function(target, proto) {\\n try {\\n setPrototypeOf(target, proto);\\n return true;\\n } catch (e) {\\n return false;\\n }\\n };\\n return polyfill;\\n } else {\\n // it isn't possible to implement this method\\n return null;\\n }\\n },\\n 'es6', 'es5');\\n\",\"js/es6/set.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require es6/conformance';\\n'require es6/map';\\n'require es6/symbol';\\n'require util/defines';\\n'require util/polyfill';\\n\\n$jscomp.polyfill('Set',\\n /**\\n * @param {*} NativeSet\\n * @return {*}\\n * @suppress {reportUnknownTypes}\\n */\\n function(NativeSet) {\\n\\n /**\\n * Checks conformance of the existing Set.\\n * @return {boolean} True if the browser's implementation conforms.\\n * @suppress {missingProperties} \\\"entries\\\" unknown prototype\\n */\\n function isConformant() {\\n if ($jscomp.ASSUME_NO_NATIVE_SET ||\\n !NativeSet ||\\n typeof NativeSet != \\\"function\\\" ||\\n !NativeSet.prototype.entries ||\\n typeof Object.seal != 'function') {\\n return false;\\n }\\n // Some implementations don't support constructor arguments.\\n try {\\n NativeSet = /"; +a.a+="** @type {function(new: Set, !Iterator=)} */ (NativeSet);\\n var value = Object.seal({x: 4});\\n var set = new NativeSet($jscomp.makeIterator([value]));\\n if (!set.has(value) || set.size != 1 || set.add(value) != set ||\\n set.size != 1 || set.add({x: 4}) != set || set.size != 2) {\\n return false;\\n }\\n var iter = set.entries();\\n var item = iter.next();\\n if (item.done || item.value[0] != value || item.value[1] != value) {\\n return false;\\n }\\n item = iter.next();\\n if (item.done || item.value[0] == value || item.value[0].x != 4 ||\\n item.value[1] != item.value[0]) {\\n return false;\\n }\\n return iter.next().done;\\n } catch (err) { // This should hopefully never happen, but let's be safe.\\n return false;\\n }\\n }\\n\\n if ($jscomp.USE_PROXY_FOR_ES6_CONFORMANCE_CHECKS) {\\n if (NativeSet && $jscomp.ES6_CONFORMANCE) return NativeSet;\\n } else {\\n if (isConformant()) return NativeSet;\\n }\\n\\n // We depend on Symbol.iterator, so ensure it's loaded.\\n $jscomp.initSymbolIterator();\\n\\n\\n\\n /**\\n * Polyfill for the global Map data type.\\n * @constructor\\n * @struct\\n * @extends {Set}\\n * @implements {Iterable}\\n * @template KEY, VALUE\\n * @param {!Iterable|!Array|null=} opt_iterable\\n * Optional data to populate the set.\\n */\\n // TODO(sdh): fix param type if heterogeneous arrays ever supported.\\n var PolyfillSet = function(opt_iterable) {\\n /** @private @const {!Map} */\\n this.map_ = new Map();\\n if (opt_iterable) {\\n var iter = $jscomp.makeIterator(opt_iterable);\\n var entry;\\n while (!(entry = iter.next()).done) {\\n var item = /** @type {!IIterableResult} */ (entry).value;\\n this.add(item);\\n }\\n }\\n // Note: this property should not be changed. If we're willing to give up\\n // ES3 support, we could define it as a property directly. It should be\\n // marked readonly if such an annotation ever comes into existence.\\n this.size = this.map_.size;\\n };\\n\\n\\n /** @override */\\n PolyfillSet.prototype.add = function(value) {\\n // normalize -0/+0 to +0\\n value = value === 0 ? 0 : value;\\n this.map_.set(value, value);\\n this.size = this.map_.size;\\n return this;\\n };\\n\\n\\n /** @override */\\n PolyfillSet.prototype.delete = function(value) {\\n var result = this.map_.delete(value);\\n this.size = this.map_.size;\\n return result;\\n };\\n\\n\\n /** @override */\\n PolyfillSet.prototype.clear = function() {\\n this.map_.clear();\\n this.size = 0;\\n };\\n\\n\\n /** @override */\\n PolyfillSet.prototype.has = function(value) {\\n return this.map_.has(value);\\n };\\n\\n\\n /** @override */\\n PolyfillSet.prototype.entries = function() {\\n return this.map_.entries();\\n };\\n\\n\\n /** @override */\\n PolyfillSet.prototype.values = function() {\\n return this.map_.values();\\n };\\n\\n\\n /** @override */\\n PolyfillSet.prototype.keys = PolyfillSet.prototype.values;\\n\\n\\n /** @type {?} */ (PolyfillSet.prototype)[Symbol.iterator] =\\n PolyfillSet.prototype.values;\\n\\n\\n /** @override */\\n PolyfillSet.prototype.forEach = function(callback, opt_thisArg) {\\n var set = this;\\n this.map_.forEach(function(value) {\\n return callback.call(/** @type {?} */ (opt_thisArg), value, value, set);\\n });\\n };\\n\\n\\n return PolyfillSet;\\n}, 'es6', 'es3');\\n\",\"js/es6/string.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Brings in all ES6 String polyfills.\\n */\\n'require es6/string/codepointat';\\n'require es6/string/endswith';\\n'require es6/string/fromcodepoint';\\n'require es6/string/includes';\\n'require es6/string/padend';\\n'require es6/string/padstart';\\n'require es6/string/repeat';\\n'require es6/string/startswith';\\n\",\"js/es6/string/codepointat.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/checkstringargs';\\n'require util/polyfill';\\n\\n$jscomp.polyfill('String.prototype.codePointAt', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Returns the UTF-16 codepoint at the given index.\\n *\\n *

      Polyfills the instance method String.prototype.codePointAt().\\n *\\n * @this {string}\\n * @param {number} position\\n * @return {number|undefined} The codepoint.\\n */\\n var polyfill = function(position) {\\n // NOTE: this is taken from v8's harmony-string.js StringCodePointAt\\n 'use strict';\\n var string = $jscomp.checkStringArgs(this, null, 'codePointAt');\\n var size = string.length;\\n // Make 'position' a number (non-number coerced to NaN and then or to zero).\\n position = Number(position) || 0;\\n if (!(position >= 0 && position < size)) {\\n return void 0;\\n }\\n // Truncate 'position' to an integer.\\n position = position | 0;\\n var first = string.charCodeAt(position);\\n if (first < 0xD800 || first > 0xDBFF || position + 1 === size) {\\n return first;\\n }\\n var second = string.charCodeAt(position + 1);\\n if (second < 0xDC00 || second > 0xDFFF) {\\n return first;\\n }\\n return (first - 0xD800) * 0x400 + second + 0x2400;\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/string/endswith.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/checkstringargs';\\n'require util/polyfill';\\n\\n$jscomp.polyfill('String.prototype.endsWith', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Tests whether the string ends with a given substring.\\n *\\n *

      Polyfills the instance method String.prototype.endsWith().\\n *\\n * @this {string}\\n * @param {string} searchString\\n * @param {number=} opt_position\\n * @return {boolean}\\n */\\n var polyfill = function(searchString, opt_position) {\\n 'use strict';\\n var string = $jscomp.checkStringArgs(this, searchString, 'endsWith');\\n searchString = searchString + '';\\n if (opt_position === void 0) opt_position = string.length;\\n var i = Math.max(0, Math.min(opt_position | 0, string.length));\\n var j = searchString.length;\\n while (j > 0 && i > 0) {\\n if (string[--i] != searchString[--j]) return false;\\n }\\n return j <= 0;\\n };\\nreturn polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/string/fromcodepoint.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/polyfill';\\n\\n$jscomp.polyfill('String.fromCodePoint', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Creates a new string from the given codepoints.\\n *\\n *

      Polyfills the static function String.fromCodePoint().\\n *\\n * @param {...number} var_args\\n * @return {string}\\n */\\n var polyfill = function(var_args) {\\n // Note: this is taken from v8's harmony-string.js StringFromCodePoint.\\n var result = '';\\n for (var i = 0; i < arguments.length; i++) {\\n var code = Number(arguments[i]);\\n if (code < 0 || code > 0x10FFFF || code !== Math.floor(code)) {\\n throw new RangeError('invalid_code_point ' + code);\\n }\\n if (code <= 0xFFFF) {\\n result += String.fromCharCode(code);\\n } else {\\n code -= 0x10000;\\n result += String.fromCharCode((code >>> 10) & 0x3FF | 0xD800);\\n result += String.fromCharCode(code & 0x3FF | 0xDC00);\\n }\\n }\\n return result;\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/string/includes.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/checkstringargs';\\n'require util/polyfill';\\n\\n$jscomp.polyfill('String.prototype.includes', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Searches for a substring, starting at the given position.\\n *\\n *

      Polyfills the instance method String.prototype.includes().\\n *\\n * @this {string}\\n * @param {string} searchString\\n * @param {number=} opt_position\\n * @return {boolean}\\n */\\n var polyfill = function(searchString, opt_position) {\\n 'use strict';\\n var string = $jscomp.checkStringArgs(this, searchString, 'includes');\\n return string.indexOf(searchString, opt_position || 0) !== -1;\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/string/padend.js\":\"/*\\n * Copyright 2017 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/checkstringargs';\\n'require util/stringpadding';\\n'require util/polyfill';\\n\\n$jscomp.polyfill('String.prototype.padEnd', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Polyfills String.prototype.padEnd.\\n *\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd\\n *\\n * @this {string}\\n * @param {number} targetLength\\n * @param {string=} opt_padString\\n * @return {string}\\n */\\n var padEnd = function(targetLength, opt_padString) {\\n var string = $jscomp.checkStringArgs(this, null, 'padStart');\\n var padLength = targetLength - string.length;\\n return string + $jscomp.stringPadding(opt_padString, padLength);\\n };\\n\\n return padEnd;\\n}, 'es8', 'es3');\\n\",\"js/es6/string/padstart.js\":\"/*\\n * Copyright 2017 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/checkstringargs';\\n'require util/polyfill';\\n'require util/stringpadding';\\n\\n$jscomp.polyfill('String.prototype.padStart', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Polyfills String.prototype.padStart.\\n *\\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart\\n *\\n * @this {string}\\n * @param {number} targetLength\\n * @param {string=} opt_padString\\n * @return {string}\\n */\\n var padStart = function(targetLength, opt_padString) {\\n var string = $jscomp.checkStringArgs(this, null, 'padStart');\\n var padLength = targetLength - string.length;\\n return $jscomp.stringPadding(opt_padString, padLength) + string;\\n };\\n\\n return padStart;\\n}, 'es8', 'es3');\\n\",\"js/es6/string/repeat.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/checkstringargs';\\n'require util/polyfill';\\n\\n$jscomp.polyfill('String.prototype.repeat', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Returns a new string repeated the given number of times.\\n *\\n *

      Polyfills the instance method String.prototype.repeat().\\n *\\n * @this {string}\\n * @param {number} copies\\n * @return {string}\\n */\\n var polyfill = function(copies) {\\n 'use strict';\\n var string = $jscomp.checkStringArgs(this, null, 'repeat');\\n if (copies < 0 || copies > 0x4FFFFFFF) { // impose a 1GB limit\\n throw new RangeError('Invalid count value');\\n }\\n copies = copies | 0; // cast to a signed integer.\\n var result = '';\\n while (copies) {\\n if (copies & 1) result += string;\\n if ((copies >>>= 1)) string += string;\\n }\\n return result;\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/string/startswith.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require util/checkstringargs';\\n'require util/polyfill';\\n\\n$jscomp.polyfill('String.prototype.startsWith', function(orig) {\\n if (orig) return orig;\\n\\n /**\\n * Tests whether the string starts with a given substring.\\n *\\n *

      Polyfil"; +a.a+="ls the instance method String.prototype.startsWith().\\n *\\n * @this {string}\\n * @param {string} searchString\\n * @param {number=} opt_position\\n * @return {boolean}\\n */\\n var polyfill = function(searchString, opt_position) {\\n 'use strict';\\n var string = $jscomp.checkStringArgs(this, searchString, 'startsWith');\\n searchString = searchString + '';\\n var strLen = string.length;\\n var searchLen = searchString.length;\\n var i = Math.max(\\n 0,\\n Math.min(/** @type {number} */ (opt_position) | 0, string.length));\\n var j = 0;\\n while (j < searchLen && i < strLen) {\\n if (string[i++] != searchString[j++]) return false;\\n }\\n return j >= searchLen;\\n };\\n\\n return polyfill;\\n}, 'es6', 'es3');\\n\",\"js/es6/symbol.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Symbol polyfill.\\n * @suppress {uselessCode}\\n */\\n\\n'require es6/util/arrayiterator';\\n'require util/defineproperty';\\n'require util/global';\\n\\n/** @const {string} */\\n$jscomp.SYMBOL_PREFIX = 'jscomp_symbol_';\\n\\n/**\\n * Initializes the Symbol function.\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.initSymbol = function() {\\n // Only need to do this once. All future calls are no-ops.\\n $jscomp.initSymbol = function() {};\\n\\n if (!$jscomp.global['Symbol']) {\\n $jscomp.global['Symbol'] = $jscomp.Symbol;\\n }\\n};\\n\\n\\n/**\\n * @struct @constructor\\n * @param {string} id\\n * @param {string=} opt_description\\n */\\n$jscomp.SymbolClass = function(id, opt_description) {\\n /** @private @const {string} */\\n this.$jscomp$symbol$id_ = id;\\n\\n /** @const {string|undefined} */\\n this.description;\\n\\n // description is read-only.\\n $jscomp.defineProperty(\\n this, 'description',\\n {configurable: true, writable: true, value: opt_description});\\n};\\n\\n\\n/** @override */\\n$jscomp.SymbolClass.prototype.toString = function() {\\n return this.$jscomp$symbol$id_;\\n};\\n\\n\\n/**\\n * Produces \\\"symbols\\\" (actually just unique strings).\\n * @param {string=} opt_description\\n * @return {symbol}\\n */\\n$jscomp.Symbol = /** @type {function(): !Function} */ (function() {\\n var counter = 0;\\n\\n /**\\n * @param {string=} opt_description\\n * @return {symbol}\\n * @suppress {reportUnknownTypes}\\n */\\n function Symbol(opt_description) {\\n if (/** @type {?} */ (this) instanceof Symbol) {\\n throw new TypeError('Symbol is not a constructor');\\n }\\n return /** @type {?} */ (new $jscomp.SymbolClass(\\n $jscomp.SYMBOL_PREFIX + (opt_description || '') + '_' + (counter++),\\n opt_description));\\n }\\n\\n return Symbol;\\n})();\\n\\n/**\\n * Initializes Symbol.iterator (if it's not already defined) and adds a\\n * Symbol.iterator property to the Array prototype.\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.initSymbolIterator = function() {\\n $jscomp.initSymbol();\\n var symbolIterator = $jscomp.global['Symbol'].iterator;\\n if (!symbolIterator) {\\n symbolIterator = $jscomp.global['Symbol'].iterator =\\n $jscomp.global['Symbol']('Symbol.iterator');\\n }\\n\\n if (typeof Array.prototype[symbolIterator] != 'function') {\\n $jscomp.defineProperty(\\n Array.prototype, symbolIterator, {\\n configurable: true,\\n writable: true,\\n /**\\n * @this {Array}\\n * @return {!IteratorIterable}\\n */\\n value: function() {\\n return $jscomp.iteratorPrototype(\\n $jscomp.arrayIteratorImpl(this));\\n }\\n });\\n }\\n\\n // Only need to do this once. All future calls are no-ops.\\n $jscomp.initSymbolIterator = function() {};\\n};\\n\\n\\n/**\\n * Initializes Symbol.asyncIterator (if it's not already defined)\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.initSymbolAsyncIterator = function() {\\n $jscomp.initSymbol();\\n var symbolAsyncIterator = $jscomp.global['Symbol'].asyncIterator;\\n if (!symbolAsyncIterator) {\\n symbolAsyncIterator = $jscomp.global['Symbol'].asyncIterator =\\n $jscomp.global['Symbol']('Symbol.asyncIterator');\\n }\\n\\n // Only need to do this once. All future calls are no-ops.\\n $jscomp.initSymbolAsyncIterator = function() {};\\n};\\n\\n/**\\n * Returns an iterator with the given `next` method. Passing\\n * all iterators through this function allows easily extending\\n * the definition of `%IteratorPrototype%` if methods are ever\\n * added to it in the future.\\n *\\n * @param {function(this: Iterator): T} next\\n * @return {!IteratorIterable}\\n * @template T\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.iteratorPrototype = function(next) {\\n $jscomp.initSymbolIterator();\\n\\n var iterator = {next: next};\\n /**\\n * @this {IteratorIterable}\\n * @return {!IteratorIterable}\\n */\\n iterator[$jscomp.global['Symbol'].iterator] = function() {\\n return this;\\n };\\n return /** @type {!IteratorIterable} */ (iterator);\\n};\\n\",\"js/es6/util/arrayfromiterable.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Polyfill for array destructuring.\\n */\\n'require es6/util/makeiterator';\\n'require es6/util/arrayfromiterator';\\n\\n\\n/**\\n * Copies the values from an Iterable into an Array.\\n * @param {string|!Array|!Iterable|!Arguments} iterable\\n * @return {!Array}\\n * @template T\\n */\\n$jscomp.arrayFromIterable = function(iterable) {\\n if (iterable instanceof Array) {\\n return iterable;\\n } else {\\n return $jscomp.arrayFromIterator($jscomp.makeIterator(iterable));\\n }\\n};\\n\",\"js/es6/util/arrayfromiterator.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Polyfill for array destructuring.\\n */\\n'require base';\\n\\n\\n/**\\n * Copies the values from an Iterator into an Array. The important difference\\n * between this and $jscomp.arrayFromIterable is that if the iterator's\\n * next() method has already been called one or more times, this method returns\\n * only the values that haven't been yielded yet.\\n * @param {!Iterator} iterator\\n * @return {!Array}\\n * @template T\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.arrayFromIterator = function(iterator) {\\n var i;\\n var arr = [];\\n while (!(i = iterator.next()).done) {\\n arr.push(i.value);\\n }\\n return arr;\\n};\\n\",\"js/es6/util/arrayiterator.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Utility method for creating an iterator for Arrays.\\n */\\n'require base';\\n\\n/**\\n * Returns an internal iterator from the given array.\\n * @param {!Array} array\\n * @return {function():!IIterableResult}\\n * @template T\\n */\\n$jscomp.arrayIteratorImpl = function(array) {\\n var index = 0;\\n return function() {\\n if (index < array.length) {\\n return {\\n done: false,\\n value: array[index++],\\n };\\n } else {\\n return {done: true};\\n }\\n };\\n};\\n\\n/**\\n * Returns an internal iterator from the given array.\\n * @param {!Array} array\\n * @return {!Iterator}\\n * @template T\\n */\\n$jscomp.arrayIterator = function(array) {\\n return /** @type {!Iterator} */ ({next: $jscomp.arrayIteratorImpl(array)});\\n};\\n\\n\",\"js/es6/util/assign.js\":\"/*\\n * Copyright 2018 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview\\n * @suppress {uselessCode}\\n */\\n'require util/owns';\\n\\n/**\\n * Equivalent to the Object.assign() method, but guaranteed to be available for use in code\\n * generated by the compiler.\\n *\\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign\\n *\\n * Copies values of all enumerable own properties from one or more\\n * sources to the given target object, and returns the target.\\n *\\n * @final\\n * @param {!Object} target The target object onto which to copy.\\n * @param {...?Object} var_args The source objects.\\n * @return {!Object} The target object is returned.\\n */\\n$jscomp.assign = (typeof Object.assign == 'function') ?\\n Object.assign :\\n /**\\n * @param {!Object} target\\n * @param {...?Object} var_args\\n * @return {!Object}\\n * @suppress {reportUnknownTypes}\\n */\\n function(target, var_args) {\\n for (var i = 1; i < arguments.length; i++) {\\n var source = arguments[i];\\n if (!source) continue;\\n for (var key in source) {\\n if ($jscomp.owns(source, key)) target[key] = source[key];\\n }\\n }\\n return target;\\n };\\n\",\"js/es6/util/construct.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview\\n * @suppress {uselessCode}\\n */\\n\\n'require util/objectcreate';\\n\\n/**\\n * Polyfill for Reflect.construct() method:\\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/construct\\n *\\n * Calls a constructor as with the 'new' operator.\\n * TODO(sdh): how to type 'target' with (new: TARGET) if opt_newTarget missing?\\n *\\n * @param {function(new: ?, ...?)} target The constructor to call.\\n * @param {!Array} argList The arguments as a list.\\n * @param {function(new: TARGET, ...?)=} opt_newTarget The constructor to instantiate.\\n * @return {TARGET} The result of the function call.\\n * @template TARGET\\n */\\n$jscomp.construct = /** @type {function(): !Function} */ (function() {\\n\\n // Check for https://github.com/Microsoft/ChakraCore/issues/3217\\n /** @return {boolean} */\\n function reflectConstructWorks() {\\n /** @constructor */ function Base() {}\\n /** @constructor */ function Derived() {}\\n new Base();\\n Reflect.construct(Base, [], Derived);\\n return new Base() instanceof Base;\\n }\\n\\n if (typeof Reflect != 'undefined' && Reflect.construct) {\\n if (reflectConstructWorks()) return Reflect.construct;\\n var brokenConstruct = Reflect.construct;\\n /**\\n * @param {function(new: ?, ...?)} target The constructor to call.\\n * @param {!Array} argList The arguments as a list.\\n * @param {function(new: TARGET, ...?)=} opt_newTarget The constructor to instantiate.\\n * @return {TARGET} The result of the function call.\\n * @template TARGET\\n * @suppress {reportUnknownTypes}\\n */\\n var patchedConstruct = function(target, argList, opt_newTarget) {\\n var out = brokenConstruct(target, argList);\\n if (opt_newTarget) Reflect.setPrototypeOf(out, opt_newTarget.prototype);\\n return out;\\n };\\n return patchedConstruct;\\n }\\n\\n /**\\n * @param {function(new: ?, ...?)} target The constructor to call.\\n * @param {!Array} argList The arguments as a list.\\n * @param {function(new: TARGET, ...?)=} opt_newTarget The constructor to instantiate.\\n * @return {TARGET} The result of the function call.\\n * @template TARGET\\n * @suppress {reportUnknownTypes}\\n */\\n function construct(target, argList, opt_newTarget) {\\n if (opt_newTarget === undefined) opt_newTarget = target;\\n var proto = opt_newTarget.prototype || Object.prototype;\\n var obj = $jscomp.objectCreate(proto);\\n var apply = Function.prototype.apply;\\n var out = apply.call(target, obj, argList);\\n return out || obj;\\n }\\n return construct;\\n})();\\n\",\"js/es6/util/inherits.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Polyfill for ES6 extends keyword.\\n * @suppress {uselessCode}\\n */\\n'require base';\\n'require util/objectcreate';\\n'require es6/util/setprototypeof';\\n\\n\\n/**\\n * Inherit the prototype methods and static methods from one constructor\\n * into another.\\n *\\n * This wires up the prototype chain (like goog.inherits) and copies static\\n * properties, for ES6-to-ES{3,5} transpilation.\\n *\\n * Usage:\\n *

      \\n *   function ParentClass() {}\\n *\\n *   // Regular method.\\n *   ParentClass.prototype.foo = function(a) {};\\n *\\n *   // Static method.\\n *   ParentClass.bar = function() {};\\n *\\n *   function ChildClass() {\\n *     ParentClass.call(this);\\n *   }\\n *   $jscomp.inherits(ChildClass, ParentClass);\\n *\\n *   var child = new ChildClass();\\n *   child.foo();\\n *   ChildClass.bar();  // Static inheritance.\\n * 
      \\n *\\n * @param {!Function} childCtor Child class.\\n * @param {!Function} parentCtor Parent class.\\n * @suppress {strictMissingProperties} 'superClass_' is not defined on Function\\n */\\n$jscomp.inherits = function(childCtor, parentCtor) {\\n childCtor.prototype = $jscomp.objectCreate(parentCtor.prototype);\\n /** @override */ childCtor.proto"; +a.a+="type.constructor = childCtor;\\n if ($jscomp.setPrototypeOf) {\\n // avoid null dereference warning\\n /** @const {!Function} */\\n var setPrototypeOf = $jscomp.setPrototypeOf;\\n setPrototypeOf(childCtor, parentCtor);\\n } else {\\n // setPrototypeOf is not available so we need to copy the static\\n // methods to the child\\n for (var p in parentCtor) {\\n if (p == 'prototype') {\\n // Don't copy parentCtor.prototype to childCtor.\\n continue;\\n }\\n if (Object.defineProperties) {\\n var descriptor = Object.getOwnPropertyDescriptor(parentCtor, p);\\n if (descriptor) {\\n Object.defineProperty(childCtor, p, descriptor);\\n }\\n } else {\\n // Pre-ES5 browser. Just copy with an assignment.\\n childCtor[p] = parentCtor[p];\\n }\\n }\\n }\\n\\n childCtor.superClass_ = parentCtor.prototype;\\n};\\n\",\"js/es6/util/iteratorfromarray.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Utilities for iterator-returning methods.\\n */\\n'require es6/symbol';\\n\\n\\n/**\\n * Creates an iterator from an array-like, with a transformation function.\\n * @param {!IArrayLike} array\\n * @param {function(number, INPUT): OUTPUT} transform\\n * @return {!IteratorIterable}\\n * @template INPUT, OUTPUT\\n * @suppress {checkTypes|reportUnknownTypes}\\n */\\n$jscomp.iteratorFromArray = function(array, transform) {\\n $jscomp.initSymbolIterator();\\n // NOTE: IE8 doesn't support indexing from boxed Strings.\\n if (array instanceof String) array = array + '';\\n var i = 0;\\n var iter = {\\n next: function() {\\n if (i < array.length) {\\n var index = i++;\\n return {value: transform(index, array[index]), done: false};\\n }\\n iter.next = function() { return {done: true, value: void 0}; };\\n return iter.next();\\n }\\n };\\n iter[Symbol.iterator] = function() { return iter; };\\n return iter;\\n};\\n\",\"js/es6/util/makeasynciterator.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Polyfill for for-of loops.\\n */\\n'require es6/symbol';\\n'require es6/util/makeiterator';\\n\\n/**\\n * Creates an iterator for the given iterable.\\n *\\n * @param {string|!AsyncIterable|!Iterable|!Iterator|!Arguments} iterable\\n * @return {!AsyncIteratorIterable}\\n * @template T\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.makeAsyncIterator = function(iterable) {\\n $jscomp.initSymbolAsyncIterator();\\n var asyncIteratorFunction = (iterable)[Symbol.asyncIterator];\\n if (asyncIteratorFunction !== undefined) {\\n return asyncIteratorFunction.call(iterable);\\n }\\n return new $jscomp.AsyncIteratorFromSyncWrapper($jscomp.makeIterator(\\n /** @type {string|!Iterable|!Iterator|!Arguments} */\\n (iterable)));\\n};\\n\\n/**\\n *\\n * @param {!Iterator} iterator\\n * @constructor\\n * @implements {AsyncIteratorIterable}\\n * @template T\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.AsyncIteratorFromSyncWrapper = function(iterator) {\\n /**\\n * @return {!AsyncIterator}\\n */\\n this[Symbol.asyncIterator] = function() {\\n return this;\\n };\\n\\n /**\\n * @return {!Iterator>>}\\n */\\n this[Symbol.iterator] = function() {\\n return iterator;\\n };\\n\\n /**\\n * @param {?=} param\\n * @return {!Promise>}\\n */\\n this.next = function(param) {\\n return Promise.resolve(iterator.next(param));\\n };\\n\\n if (iterator['throw'] !== undefined) {\\n /**\\n * @param {?} param\\n * @return {!Promise>}\\n */\\n this['throw'] = function(param) {\\n return Promise.resolve(iterator['throw'](param));\\n };\\n }\\n\\n if (iterator['return'] !== undefined) {\\n /**\\n * @param {T} param\\n * @return {!Promise>}\\n */\\n this['return'] = function(param) {\\n return Promise.resolve(iterator['return'](param));\\n };\\n }\\n};\\n\",\"js/es6/util/makeiterator.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Polyfill for for-of loops.\\n */\\n'require es6/util/arrayiterator';\\n\\n/**\\n * Creates an iterator for the given iterable. This iterator should never\\n * be exposed to user code.\\n *\\n * @param {string|!Iterable|!Iterator|!Arguments} iterable\\n * @return {!Iterator}\\n * @template T\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.makeIterator = function(iterable) {\\n // NOTE: Disabling typechecking because [] not allowed on @struct.\\n var iteratorFunction = typeof Symbol != 'undefined' && Symbol.iterator &&\\n (/** @type {?} */ (iterable)[Symbol.iterator]);\\n return iteratorFunction ? iteratorFunction.call(iterable) :\\n $jscomp.arrayIterator(/** @type {!Array} */ (iterable));\\n};\\n\",\"js/es6/util/setprototypeof.js\":\"/*\\n * Copyright 2017 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview\\n * @suppress {uselessCode}\\n */\\n\\n/**\\n * @suppress {missingProperties,reportUnknownTypes}\\n * @return {boolean}\\n */\\n$jscomp.underscoreProtoCanBeSet = function() {\\n var x = {a: true};\\n var y = {};\\n try {\\n y.__proto__ = x;\\n return y.a;\\n } catch (e) {\\n // __proto__ property is readonly (possibly IE 10?)\\n }\\n return false;\\n};\\n\\n/**\\n * If we can implement it, this will be a function that attempts to set the\\n * prototype of an object, otherwise it will be `null`.\\n *\\n * It returns the first argument if successful. Throws a `TypeError` if the\\n * object is not extensible.\\n *\\n * @type {null|function(!Object, ?Object): !Object}\\n */\\n$jscomp.setPrototypeOf = (typeof Object.setPrototypeOf == 'function') ?\\n Object.setPrototypeOf :\\n $jscomp.underscoreProtoCanBeSet() ?\\n function(target, proto) {\\n target.__proto__ = proto;\\n if (target.__proto__ !== proto) {\\n throw new TypeError(target + ' is not extensible');\\n }\\n return target;\\n } :\\n null;\\n\",\"js/es6/weakmap.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require es6/conformance';\\n'require es6/util/makeiterator';\\n'require util/defineproperty';\\n'require util/owns';\\n'require util/polyfill';\\n\\n$jscomp.polyfill('WeakMap',\\n /**\\n * @param {*} NativeWeakMap\\n * @return {*}\\n * @suppress {reportUnknownTypes}\\n */\\n function(NativeWeakMap) {\\n /**\\n * Checks conformance of the existing WeakMap.\\n * @return {boolean} True if the browser's implementation conforms.\\n */\\n function isConformant() {\\n if (!NativeWeakMap || !Object.seal) return false;\\n try {\\n var x = Object.seal({});\\n var y = Object.seal({});\\n var map = new /** @type {function(new: WeakMap, !Array)} */ (\\n NativeWeakMap)([[x, 2], [y, 3]]);\\n if (map.get(x) != 2 || map.get(y) != 3) return false;\\n map.delete(x);\\n map.set(y, 4);\\n return !map.has(x) && map.get(y) == 4;\\n } catch (err) { // This should hopefully never happen, but let's be safe.\\n return false;\\n }\\n }\\n if ($jscomp.USE_PROXY_FOR_ES6_CONFORMANCE_CHECKS) {\\n if (NativeWeakMap && $jscomp.ES6_CONFORMANCE) return NativeWeakMap;\\n } else {\\n if (isConformant()) return NativeWeakMap;\\n }\\n\\n var prop = '$jscomp_hidden_' + Math.random();\\n\\n /** @constructor */\\n function WeakMapMembership() {}\\n\\n /**\\n * Inserts the hidden property into the target.\\n * @param {!Object} target\\n */\\n function insert(target) {\\n if (!$jscomp.owns(target, prop)) {\\n var obj = new WeakMapMembership();\\n // TODO(sdh): This property will be enumerated in IE8. If this becomes\\n // a problem, we could avoid it by copying an infrequently-used non-enum\\n // method (like toLocaleString) onto the object itself and encoding the\\n // property on the copy instead. This codepath must be easily removable\\n // if IE8 support is not needed.\\n $jscomp.defineProperty(target, prop, {value: obj});\\n }\\n }\\n\\n /**\\n * Monkey-patches the freezing methods to ensure that the hidden\\n * property is added before any freezing happens.\\n * @param {string} name\\n */\\n function patch(name) {\\n var prev = Object[name];\\n if (prev) {\\n Object[name] = function(target) {\\n if (target instanceof WeakMapMembership) {\\n return target;\\n } else {\\n insert(target);\\n return prev(target);\\n }\\n };\\n }\\n }\\n patch('freeze');\\n patch('preventExtensions');\\n patch('seal');\\n // Note: no need to patch Reflect.preventExtensions since the polyfill\\n // just calls Object.preventExtensions anyway (and if it's not polyfilled\\n // then neither is WeakMap).\\n\\n var index = 0;\\n\\n /**\\n * Polyfill for WeakMap:\\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap\\n *\\n * This implementation is as non-leaky as possible, due to patching\\n * the freezing and sealing operations. It does not include any logic\\n * to handle cases where a key was somehow made non-extensible without\\n * the special hidden property being added. It takes some care to ensure\\n * the hidden property is not enumerated over nor discoverable, though\\n * it's not completely secure (particularly in IE8).\\n *\\n * @constructor\\n * @extends {WeakMap}\\n * @template KEY, VALUE\\n * @param {!Iterator>|!Array>|null=}\\n * opt_iterable Optional initial data.\\n */\\n var PolyfillWeakMap = function(opt_iterable) {\\n /** @private @const {string} */\\n this.id_ = (index += (Math.random() + 1)).toString();\\n\\n if (opt_iterable) {\\n var iter = $jscomp.makeIterator(opt_iterable);\\n var entry;\\n while (!(entry = iter.next()).done) {\\n var item = entry.value;\\n this.set(/** @type {KEY} */ (item[0]), /** @type {VALUE} */ (item[1]));\\n }\\n }\\n };\\n\\n /** @override */\\n PolyfillWeakMap.prototype.set = function(key, value) {\\n insert(key);\\n if (!$jscomp.owns(key, prop)) {\\n // NOTE: If the insert() call fails on the key, but the property\\n // has previously successfully been added higher up the prototype\\n // chain, then we'll silently misbehave. Instead, throw immediately\\n // before doing something bad. If this becomes a problem (e.g. due\\n // to some rogue frozen objects) then we may need to add a slow and\\n // leaky fallback array to each WeakMap instance, as well as extra\\n // logic in each accessor to use it (*only*) when necessary.\\n throw new Error('WeakMap key fail: ' + key);\\n }\\n key[prop][this.id_] = value;\\n return this;\\n };\\n\\n /** @override */\\n PolyfillWeakMap.prototype.get = function(key) {\\n return $jscomp.owns(key, prop) ? key[prop][this.id_] : undefined;\\n };\\n\\n /** @override */\\n PolyfillWeakMap.prototype.has = function(key) {\\n return $jscomp.owns(key, prop) && $jscomp.owns(key[prop], this.id_);\\n };\\n\\n /** @override */\\n PolyfillWeakMap.prototype.delete = function(key) {\\n if (!$jscomp.owns(key, prop) ||\\n !$jscomp.owns(key[prop], this.id_)) {\\n return false;\\n }\\n return delete key[prop][this.id_];\\n };\\n\\n return PolyfillWeakMap;\\n}, 'es6', 'es3');\\n\",\"js/es6/weakset.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n'require es6/conformance';\\n'require es6/util/makeiterator';\\n'require util/polyfill';\\n'require es6/weakmap';\\n\\n$jscomp.polyfill('WeakSet',\\n /**\\n * @param {*} NativeWeakSet\\n * @return {*}\\n * @suppress {reportUnknownTypes}\\n */\\n function(NativeWeakSet) {\\n /**\\n * Checks conformance of the existing WeakSet.\\n * @return {boolean} True if the browser's implementation conforms.\\n */\\n function isConformant() {\\n if (!NativeWeakSet || !Object.seal) return false;\\n try {\\n var x = Object.seal({});\\n var y = Object.seal({});\\n var set = new /** @type {function(new: WeakSet, !Array)} */ (\\n NativeWeakSet)([x]);\\n if (!set.has(x) || set.has(y)) return false;\\n set.delete(x);\\n set.add(y);\\n return !set.has(x) && set.has(y);\\n } catch (err) { // This should hopefully never happen, but let's be safe.\\n return false;\\n }\\n }\\n if ($jscomp.USE_PROXY_FOR_ES6_CONFORMANCE_CHECKS) {\\n if (NativeWeakSet && $jscomp.ES6_CONFORMANCE) return NativeWeakSet;\\n } else {\\n if (isConformant()) return NativeWeakSet;\\n }\\n\\n /**\\n * @constructor\\n * @extends {WeakSet}\\n * @template TYPE\\n * @param {!Iterator|!Array|null=} opt_iterable\\n */\\n var PolyfillWeakSet = function(opt_iterable) {\\n /** @private @const {!WeakMap} */\\n this.map_ = new WeakMap();\\n\\n if (opt_iterable) {\\n var iter = $jscomp.makeIterator(opt_iterable);\\n var entry;\\n while (!(entry = iter.next()).done) {\\n var item = entry.value;\\n this.ad"; +a.a+="d(item);\\n }\\n }\\n };\\n\\n /** @override */\\n PolyfillWeakSet.prototype.add = function(elem) {\\n this.map_.set(elem, true);\\n return this;\\n };\\n\\n /** @override */\\n PolyfillWeakSet.prototype.has = function(elem) {\\n return this.map_.has(elem);\\n };\\n\\n /** @override */\\n PolyfillWeakSet.prototype.delete = function(elem) {\\n return this.map_.delete(elem);\\n };\\n\\n return PolyfillWeakSet;\\n}, 'es6', 'es3');\\n\",\"js/es6_dart_runtime.js\":\"/*\\n * Copyright 2014 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Additional runtime functions required for transpilation from\\n * ES6 to ES5 of code generated by the Dart Dev Compiler.\\n *\\n * Note that DDC's output cannot currently be lowered to ES3 (heavy use of\\n * getters or setters, including in the runtime), so these helpers make no\\n * attempt of fallback behaviour when methods like Object.getPrototypeOf or\\n * Object.getOwnPropertyDescriptor are undefined (unlike helpers in es6/*.js).\\n *\\n * @author ochafik@google.com (Olivier Chafik)\\n */\\n'require base';\\n\\n/**\\n * Gets a property descriptor for a target instance, skipping its class\\n * and walking up the super-classes hierarchy.\\n *\\n * @private\\n * @param {!Object} target\\n * @param {!string} name\\n * @return {?}\\n */\\n$jscomp.getSuperPropertyDescriptor_ = function(target, name) {\\n var getPrototypeOf = Object.getPrototypeOf;\\n var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;\\n var cls = getPrototypeOf(target);\\n while (cls != null) {\\n cls = getPrototypeOf(cls);\\n if (cls != null) {\\n var desc = getOwnPropertyDescriptor(cls, name);\\n if (desc != null) {\\n return desc;\\n }\\n }\\n }\\n return undefined;\\n};\\n\\n/**\\n * Gets a property of a target instance using its super class getter or value,\\n * or returns undefined if that property is not defined on any ancestor.\\n *\\n * @param {!Object} target\\n * @param {!string} propertyName\\n * @return {*}\\n */\\n$jscomp.superGet = function(target, propertyName) {\\n var desc = $jscomp.getSuperPropertyDescriptor_(target, propertyName);\\n return desc && (desc.get ? desc.get.call(target) : desc.value);\\n};\\n\\n/**\\n * Sets a property on a target instance using its super setter if is defined\\n * on any ancestor, or setting it as a simple property on the target otherwise.\\n *\\n * @template T\\n * @param {!Object} target\\n * @param {!string} propertyName\\n * @param {T} value\\n * @return {T}\\n */\\n$jscomp.superSet = function(target, propertyName, value) {\\n var desc = $jscomp.getSuperPropertyDescriptor_(target, propertyName);\\n if (desc) {\\n if (!desc.set) {\\n throw new TypeError('No setter for super.' + propertyName);\\n }\\n desc.set.call(target, value);\\n } else {\\n target[propertyName] = value;\\n }\\n return value;\\n};\\n\",\"js/es6_runtime.js\":\"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Assorted runtime logic code and polyfills.\\n * @suppress {uselessCode}\\n */\\n'require es6/array';\\n'require es6/async_generator_wrapper';\\n'require es6/execute_async_generator';\\n'require es6/generator_engine';\\n'require es6/map';\\n'require es6/math';\\n'require es6/number';\\n'require es6/object';\\n'require es6/promise';\\n'require es6/reflect';\\n'require es6/set';\\n'require es6/string';\\n'require es6/symbol';\\n'require es6/util/arrayfromiterable';\\n'require es6/util/arrayfromiterator';\\n'require es6/util/inherits';\\n'require es6/util/iteratorfromarray';\\n'require es6/util/makeiterator';\\n'require es6/weakmap';\\n'require es6/weakset';\\n\",\"js/modules.js\":\"/*\\n * Copyright 2018 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview\\n * @suppress {uselessCode}\\n */\\n\\n'require es6/modules/runtime';\\n\",\"js/runtime_type_check.js\":\"/*\\n * Copyright 2010 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n\\n/**\\n * @fileoverview Provides the boilerplate code for run-time type checking.\\n *\\n * @author moedinger@google.com (Andrew Moedinger)\\n * @author nadaa@google.com (Nada Amin)\\n *\\n * @suppress {uselessCode} The require statements below are not useless.\\n */\\n'require base';\\n\\n/** @const */\\n$jscomp.typecheck = {};\\n\\n/**\\n * A state variable to suspend checking, to avoid infinite calls\\n * caused by calling checked code from the checking functions.\\n *\\n * @type {boolean}\\n */\\n$jscomp.typecheck.suspendChecking = false;\\n\\n\\n/**\\n * Log and possibly format the run-time type check warning. This\\n * function is customized at compile-time.\\n *\\n * @param {string} warning the warning to log.\\n * @param {*} expr the faulty expression.\\n */\\n$jscomp.typecheck.log = function(warning, expr) {};\\n\\n/**\\n * Checks that the given expression matches one of the given checkers,\\n * logging if not, and returning the expression regardless.\\n *\\n * @param {*} expr the expression to check.\\n * @param {!Array} checkers the checkers to\\n * use in checking, one of these has to match for checking to succeed.\\n * @return {*} the given expression back.\\n */\\n$jscomp.typecheck.checkType = function(expr, checkers) {\\n if ($jscomp.typecheck.suspendChecking) {\\n return expr;\\n }\\n $jscomp.typecheck.suspendChecking = true;\\n\\n for (var i = 0; i < checkers.length; i++) {\\n var checker = checkers[i];\\n var ok = checker.check(expr);\\n if (ok) {\\n $jscomp.typecheck.suspendChecking = false;\\n return expr;\\n }\\n }\\n\\n var warning = $jscomp.typecheck.prettify_(expr) + ' not in ' +\\n checkers.join(' ');\\n\\n $jscomp.typecheck.log(warning, expr);\\n\\n $jscomp.typecheck.suspendChecking = false;\\n return expr;\\n};\\n\\n\\n/**\\n * Prettify the given expression for printing.\\n *\\n * @param {*} expr the expression.\\n * @return {string} a string representation of the given expression.\\n * @private\\n */\\n$jscomp.typecheck.prettify_ = function(expr) {\\n var className = $jscomp.typecheck.getClassName_(expr);\\n if (className) {\\n return className;\\n }\\n try {\\n return String(expr);\\n }\\n catch (e) {}\\n return '';\\n};\\n\\n/**\\n * Gets the class name if the given expression is an object.\\n *\\n * @param {*} expr the expression.\\n * @return {string|undefined} the class name or undefined if the\\n * expression is not an object.\\n * @private\\n * @suppress {strictMissingProperties}\\n */\\n$jscomp.typecheck.getClassName_ = function(expr) {\\n var className = void 0;\\n if (typeof expr == 'object' && expr && expr.constructor) {\\n className = expr.constructor.name;\\n if (!className) {\\n var funNameRe = /function (.{1,})\\\\(/;\\n var m = (funNameRe).exec(expr.constructor.toString());\\n className = m && m.length > 1 ? m[1] : void 0;\\n }\\n }\\n return className;\\n};\\n\\n/**\\n * Interface for all checkers.\\n *\\n * @interface\\n */\\n$jscomp.typecheck.Checker = function() {};\\n\\n\\n/**\\n * Checks the given expression.\\n *\\n * @param {*} expr the expression to check.\\n * @return {boolean} whether the given expression matches this checker.\\n */\\n$jscomp.typecheck.Checker.prototype.check = function(expr) {};\\n\\n\\n\\n/**\\n * A class for all value checkers, except the null checker.\\n *\\n * @param {string} type the value type (e.g. 'number') of this checker.\\n * @constructor\\n * @implements {$jscomp.typecheck.Checker}\\n * @private\\n */\\n$jscomp.typecheck.ValueChecker_ = function(type) {\\n /**\\n * The value type of this checker.\\n * @type {string}\\n * @private\\n */\\n this.type_ = type;\\n};\\n\\n\\n/** @inheritDoc */\\n$jscomp.typecheck.ValueChecker_.prototype.check = function(expr) {\\n return typeof(expr) == this.type_;\\n};\\n\\n\\n/** @inheritDoc */\\n$jscomp.typecheck.ValueChecker_.prototype.toString = function() {\\n return 'value(' + this.type_ + ')';\\n};\\n\\n\\n\\n/**\\n * A checker class for null values.\\n *\\n * @constructor\\n * @implements {$jscomp.typecheck.Checker}\\n * @private\\n */\\n$jscomp.typecheck.NullChecker_ = function() {};\\n\\n\\n/** @inheritDoc */\\n$jscomp.typecheck.NullChecker_.prototype.check = function(expr) {\\n return expr === null;\\n};\\n\\n\\n/** @inheritDoc */\\n$jscomp.typecheck.NullChecker_.prototype.toString = function() {\\n return 'value(null)';\\n};\\n\\n\\n/**\\n * A checker class for a class defined in externs, including built-in\\n * JS types.\\n *\\n *

      If the class type is undefined, then checking is suspended to\\n * avoid spurious warnings. This is necessary because some externs\\n * types are not defined in all browsers. For example, Window is not\\n * defined Chrome, as window has the type DOMWindow.\\n *\\n *

      Another subtlety is that a built-in type may be referenced in a\\n * different frame than the one in which it was created. This causes\\n * instanceOf to return false even though the object is of the correct\\n * type. We work around this by checking as many windows as possible,\\n * redefining open on top and window to keep track of them.\\n *\\n * @param {string} className the name of the extern class to check.\\n * @constructor\\n * @implements {$jscomp.typecheck.Checker}\\n * @private\\n */\\n$jscomp.typecheck.ExternClassChecker_ = function(className) {\\n /**\\n * The name of the extern class to check.\\n * @type {string}\\n * @private\\n */\\n this.className_ = className;\\n};\\n\\n\\n/**\\n * A list of (hopefully all) open windows.\\n *\\n * @type {!Array}\\n */\\n$jscomp.typecheck.ExternClassChecker_.windows = [];\\n\\n\\n/**\\n * A list of the original open methods that have been redefined.\\n *\\n * @type {!Array}\\n */\\n$jscomp.typecheck.ExternClassChecker_.oldOpenFuns = [];\\n\\n\\n/**\\n * Redefines the open method on the given window, adding tracking.\\n *\\n * @param {!Window} win the window to track.\\n * @suppress {uselessCode}\\n */\\n$jscomp.typecheck.ExternClassChecker_.trackOpenOnWindow = function(win) {\\n // Declare the property we add to the window object.\\n // NOTE: we add a declaration in a \\\"if (false) ...\\\" to ensure we\\n // don't reference \\\"Window\\\" on platforms that don't have a global\\n // Window object (node, service workers, etc).\\n if (false) {\\n /** @type {boolean} */\\n Window.prototype.tracked;\\n }\\n\\n if (win.tracked) {\\n return;\\n }\\n\\n win.tracked = true;\\n\\n var key = $jscomp.typecheck.ExternClassChecker_.oldOpenFuns.length;\\n\\n $jscomp.typecheck.ExternClassChecker_.oldOpenFuns.push(win.open);\\n $jscomp.typecheck.ExternClassChecker_.windows.push(win);\\n\\n win.open = function() {\\n var w = $jscomp.typecheck.ExternClassChecker_.oldOpenFuns[key].apply(\\n this, arguments);\\n $jscomp.typecheck.ExternClassChecker_.trackOpenOnWindow(w);\\n return w;\\n };\\n};\\n\\n\\n/**\\n * Returns the global 'this' object. This will normally be the same as 'window'\\n * but when running in a worker thread, the DOM is not available.\\n *\\n * This does not work when strict mode is enabled.\\n *\\n * @return {!Window}\\n * @private\\n */\\n$jscomp.typecheck.ExternClassChecker_.getGlobalThis_ = function() {\\n return (function() { return this; }).call(null);\\n};\\n\\n\\n// Install listeners on the global 'this' object.\\n(function() {\\n var globalThis = $jscomp.typecheck.ExternClassChecker_.getGlobalThis_();\\n $jscomp.typecheck.ExternClassChecker_.trackOpenOnWindow(globalThis);\\n\\n var theTop = globalThis['top'];\\n if (theTop) {\\n $jscomp.typecheck.ExternClassChecker_.trackOpenOnWindow(theTop);\\n }\\n})();\\n\\n\\n/** @inheritDoc */\\n$jscomp.typecheck.ExternClassChecker_.prototype.check = function(expr) {\\n var classTypeDefined = [ false ];\\n for (var i = 0; i < $jscomp.typecheck.ExternClassChecker_.windows.length;\\n i++) {\\n var w = $jscomp.typecheck.ExternClassChecker_.windows[i];\\n if (this.checkWindow_(w, expr, classTypeDefined)) {\\n return true;\\n }\\n }\\n return !classTypeDefined[0];\\n};\\n\\n\\n/** @inheritDoc */\\n$jscomp.typecheck.ExternClassChecker_.prototype.toString = function() {\\n return 'ext_class(' + this.className_ + ')';\\n};\\n\\n\\n/**\\n * Checks whether the given expression is an instance of this extern\\n * class in this window or any of its frames and subframes.\\n *\\n * @param {!Window} w the window to start checking from.\\n * @param {*} expr the expression to check.\\n * @param {!Array} classTypeDefined a wrapped boolean\\n * updated to indicate whether the class type was seen in any frame.\\n * @return {boolean} true if the given expression is an instance of this class.\\n * @private\\n */\\n$jscomp.typecheck.ExternClassChecker_.prototype.checkWindow_ =\\n function(w, expr, classTypeDefined) {\\n var classType = /** @type {function(new: ?)} */ (w[this.className_]);\\n classTypeDefined[0] = classTypeDefined[0] || !!classType;\\n if (classType && expr instanceof classType) {\\n return true;\\n }\\n for (var i = 0; i < w.length; i++) {\\n if (this.checkWindow_(w.frames[i], expr, classTypeDefined)) {\\n return true;\\n }\\n }\\n return false;\\n};\\n\\n\\n\\n/**\\n * A class for all checkers of user-defined classes.\\n *\\n * @param {string} className name of the class to check.\\n * @constructor\\n * @implements {$jscomp.typecheck.Checker}\\n * @private\\n */\\n$jscomp.typecheck.ClassChecker_ = function(className) {\\n\\n /**\\n * The name of the class to check.\\n * @type {string}\\n * @private\\n */\\n this.className_ = className;\\n};\\n\\n\\n/** @inheritDoc */\\n$jscomp.typecheck.ClassChecker_.prototype.check = function(expr) {\\n return !!(expr && expr['instance_of__' + this.className_]);\\n};\\n\\n\\n/** @inheritDoc */\\n$jscomp.typecheck.ClassChecker_.prototype.toString = function() {\\n return 'class(' + this.className_ + ')';\\n};\\n\\n\\n\\n/**\\n * A class for all checkers of user-defined interfaces.\\n *\\n * @param {string} interfaceName name of the interface to check.\\n * @constructor\\n * @implements {$jscomp.typecheck.Checker}\\n * @private\\n */\\n$jscomp.typecheck.InterfaceChecker_ = function(interfaceName) {\\n\\n /**\\n * The name of the interface to check.\\n * @type {string}\\n * @private\\n */\\n this.interfaceName_ = interfaceName;\\n};\\n\\n\\n/** @inheritDoc */\\n$jscomp.typecheck.InterfaceChecker_.prototype.check = function(expr) {\\n return !!(expr && expr['implements__' + this.interfaceName_]);\\n};\\n\\n\\n/** @inheritDoc *"; +a.a+='/\\n$jscomp.typecheck.InterfaceChecker_.prototype.toString = function() {\\n return \'interface(\' + this.interfaceName_ + \')\';\\n};\\n\\n\\n\\n/**\\n * A checker for object types (possibly with non-standard prototype: might not\\n * inherit from Object).\\n *\\n * @constructor\\n * @implements {$jscomp.typecheck.Checker}\\n * @private\\n */\\n$jscomp.typecheck.ObjectChecker_ = function() {};\\n\\n\\n/** @inheritDoc */\\n$jscomp.typecheck.ObjectChecker_.prototype.check = function(expr) {\\n return (typeof(expr) == \'object\' || typeof(expr) == \'function\') && !!expr;\\n};\\n\\n\\n/** @inheritDoc */\\n$jscomp.typecheck.ObjectChecker_.prototype.toString = function() {\\n return \'value(object)\';\\n};\\n\\n\\n\\n/**\\n * A checker for null values.\\n *\\n * @type {!$jscomp.typecheck.Checker} a checker.\\n */\\n$jscomp.typecheck.nullChecker = new $jscomp.typecheck.NullChecker_();\\n\\n\\n/**\\n * Creates a checker for the given value type (excluding the null type).\\n *\\n * @param {string} type the value type.\\n * @return {!$jscomp.typecheck.Checker} a checker.\\n */\\n$jscomp.typecheck.valueChecker = function(type) {\\n return new $jscomp.typecheck.ValueChecker_(type);\\n};\\n\\n\\n/**\\n * Creates a checker for the given extern class name.\\n *\\n * @param {string} className the class name.\\n * @return {!$jscomp.typecheck.Checker} a checker.\\n */\\n$jscomp.typecheck.externClassChecker = function(className) {\\n return new $jscomp.typecheck.ExternClassChecker_(className);\\n};\\n\\n\\n/**\\n * Creates a checker for the given user-defined class.\\n *\\n * @param {string} className the class name.\\n * @return {!$jscomp.typecheck.Checker} a checker.\\n */\\n$jscomp.typecheck.classChecker = function(className) {\\n return new $jscomp.typecheck.ClassChecker_(className);\\n};\\n\\n\\n/**\\n * Creates a checker for the given user-defined interface.\\n *\\n * @param {string} interfaceName the interface name.\\n * @return {!$jscomp.typecheck.Checker} a checker.\\n */\\n$jscomp.typecheck.interfaceChecker = function(interfaceName) {\\n return new $jscomp.typecheck.InterfaceChecker_(interfaceName);\\n};\\n\\n\\n/**\\n * A checker for objects.\\n *\\n * @type {!$jscomp.typecheck.Checker} a checker.\\n */\\n$jscomp.typecheck.objectChecker = new $jscomp.typecheck.ObjectChecker_();\\n","js/util/checkstringargs.js":"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n\'require base\';\\n\\n\\n/**\\n * Throws if the argument is a RegExp, or if thisArg is undefined.\\n * @param {?} thisArg The \'this\' arg, which must be defined.\\n * @param {*} arg The first argument of the function, which mustn\'t be a RegExp.\\n * @param {string} func Name of the function, for reporting.\\n * @return {string} The thisArg, coerced to a string.\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.checkStringArgs = function(thisArg, arg, func) {\\n if (thisArg == null) {\\n throw new TypeError(\\n \\"The \'this\' value for String.prototype.\\" + func +\\n \' must not be null or undefined\');\\n }\\n if (arg instanceof RegExp) {\\n throw new TypeError(\\n \'First argument to String.prototype.\' + func +\\n \' must not be a regular expression\');\\n }\\n return thisArg + \'\';\\n};\\n","js/util/defineproperty.js":"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Provides methods to polyfill native objects.\\n * @suppress {reportUnknownTypes}\\n */\\n\'require util/defines\';\\n\\n\\n/**\\n * Polyfill for Object.defineProperty() method:\\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty\\n *\\n * Refuses to define properties on Array.prototype and Object.prototype,\\n * since we can\'t make them non-enumerable and this messes up peoples\' for\\n * loops. Beyond this, we simply assign values and not worry\\n * about enumerability or writeability.\\n * @param {?} target\\n * @param {string} property\\n * @param {?} descriptor\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.defineProperty =\\n $jscomp.ASSUME_ES5 || typeof Object.defineProperties == \'function\' ?\\n Object.defineProperty :\\n function(target, property, descriptor) {\\n descriptor = /** @type {!ObjectPropertyDescriptor} */ (descriptor);\\n // NOTE: This is currently never called with a descriptor outside\\n // the control of the compiler. If we ever decide to polyfill either\\n // Object.defineProperty or Reflect.defineProperty for ES3, we should\\n // explicitly check for `get` or `set` on the descriptor and throw a\\n // TypeError, since it\'s impossible to properly polyfill it.\\n if (target == Array.prototype || target == Object.prototype) return;\\n target[property] = descriptor.value;\\n };\\n","js/util/defines.js":"/*\\n * Copyright 2017 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n\'require base\';\\n\\n\\n/**\\n * Whether to assume ES5 is available. This enables removing several\\n * internal polyfills, which must otherwise be detected at runtime.\\n * @define {boolean}\\n */\\n$jscomp.ASSUME_ES5 = false;\\n\\n/**\\n * Whether to skip the conformance check and simply use the polyfill always.\\n * @define {boolean}\\n */\\n$jscomp.ASSUME_NO_NATIVE_MAP = false;\\n\\n/**\\n * Whether to skip the conformance check and simply use the polyfill always.\\n * @define {boolean}\\n */\\n$jscomp.ASSUME_NO_NATIVE_SET = false;\\n\\n/**\\n * Whether to provide an incorrect but tiny Math.fround polyfill that just\\n * returns the number given. This is usually okay to do, particularly if\\n * `Math.fround` is only used to allow the JavaScript engine to use faster\\n * 32-bit float operations, but could cause problems if program logic is\\n * dependent on floats being truncated consistently.\\n * @define {boolean}\\n */\\n$jscomp.SIMPLE_FROUND_POLYFILL = false;\\n","js/util/finddescriptor.js":"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n\'require es6/reflect/getownpropertydescriptor\';\\n\'require es6/reflect/getprototypeof\';\\n\\n\\n/**\\n * Helper function to find a descriptor.\\n * @param {!Object} target\\n * @param {string} propertyKey\\n * @return {!ObjectPropertyDescriptor|undefined}\\n */\\n$jscomp.findDescriptor = function(target, propertyKey) {\\n var /** ?Object */ obj = target;\\n while (obj) {\\n var property = Reflect.getOwnPropertyDescriptor(obj, propertyKey);\\n if (property) {\\n return property;\\n }\\n obj = Reflect.getPrototypeOf(obj);\\n }\\n return undefined;\\n};\\n","js/util/findinternal.js":"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Utility for Array methods that find elements.\\n */\\n\'require base\';\\n\\n// TODO(sdh): would be nice to template on the ARRAY type as well,\\n// so that the third arg type of callback can be refined to be\\n// exactly the same as the array type, but then there\'s no way to\\n// enforce that it must, in fact, be an array.\\n/**\\n * Internal implementation of find.\\n * @param {!IArrayLike} array\\n * @param {function(this: THIS, VALUE, number, !IArrayLike): *} callback\\n * @param {THIS} thisArg\\n * @return {{i: number, v: (VALUE|undefined)}}\\n * @template THIS, VALUE\\n * @suppress {reportUnknownTypes}\\n */\\n$jscomp.findInternal = function(array, callback, thisArg) {\\n if (array instanceof String) {\\n array = /** @type {!IArrayLike} */ (String(array));\\n }\\n var len = array.length;\\n for (var i = 0; i < len; i++) {\\n var value = array[i];\\n if (callback.call(thisArg, value, i, array)) return {i: i, v: value};\\n }\\n return {i: -1, v: void 0};\\n};\\n","js/util/global.js":"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Runtime code to store the global object.\\n */\\n\'require base\';\\n\\n\\n/**\\n * @param {!Object} maybeGlobal\\n * @return {!Object} The global object.\\n * @suppress {undefinedVars|reportUnknownTypes}\\n */\\n$jscomp.getGlobal = function(maybeGlobal) {\\n // This logic can be simplified to be made more optimizable.\\n return (typeof window != \'undefined\' && window === maybeGlobal) ?\\n maybeGlobal :\\n (typeof global != \'undefined\' && global != null) ?\\n /** @type {!Object} */ (global) :\\n maybeGlobal;\\n};\\n\\n\\n// TODO(sdh): This should be typed as \\"the global object\\" now that the type\\n// of \\"Global\\" has landed in the type system.\\n/**\\n * The global object. For browsers we could just use `this` but in Node that\\n * doesn\'t work.\\n * @const {?}\\n */\\n$jscomp.global = $jscomp.getGlobal(this);\\n","js/util/objectcreate.js":"/*\\n * Copyright 2017 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Provides a partial internal polyfill for Object.create.\\n */\\n\'require util/defines\';\\n\\n\\n/**\\n * Polyfill for Object.create() method:\\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create\\n *\\n * Does not implement the second argument.\\n * @param {!Object} prototype\\n * @return {!Object}\\n */\\n$jscomp.objectCreate =\\n ($jscomp.ASSUME_ES5 || typeof Object.create == \'function\') ?\\n Object.create :\\n function(prototype) {\\n /** @constructor */\\n var ctor = function() {};\\n ctor.prototype = prototype;\\n return new ctor();\\n };\\n","js/util/owns.js":"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n\'require base\';\\n\\n/**\\n * Synonym for Object.prototype.hasOwnProperty.call(obj, prop).\\n * @param {!Object} obj\\n * @param {string} prop\\n * @return {boolean}\\n */\\n$jscomp.owns = function(obj, prop) {\\n return Object.prototype.hasOwnProperty.call(obj, prop);\\n};\\n","js/util/polyfill.js":"/*\\n * Copyright 2016 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n/**\\n * @fileoverview Provides methods to polyfill native objects.\\n */\\n\'require util/defineproperty\';\\n\'require util/global\';\\n\\n\\n/**\\n * @param {string} target Qualified name of the class or method to polyfill,\\n * e.g. \'Array.prototype.includes\' or \'Map\'.\\n * @param {?function(*): *} polyfill A function that takes the current browser\\n * implementation of the target and returns an optional new polyfill\\n * implementation. If null is returned, then no polyfill will be added. A\\n * null argument for this parameter indicates that the function will not be\\n * polyfilled, and is only useful for `build_polyfill_table.js` bookkeeping.\\n * @param {string} fromLang The language level in which the target is expected\\n * to already be present in the browser. The compiler requires that\\n * `languageOut < fromLang` before injecting a polyfill (i.e. if the\\n * specified output language already includes the feature then there\'s no\\n * need to polyfill it).\\n * @param {string} toLang The language level required by the polyfill\\n * implementation. The compiler will issue an error if a polyfill is\\n * required, but `languageOut < toLang`. Additionally, the\\n * `build_polyfill_table.js` script audits the polyfill dependency tree to\\n * ensure that no polyfill with a lower `toLang` depends on one with a\\n * higher `toLang`.\\n * @suppress {reportUnknownTypes}\\n * @noinline\\n * NOTE: We prevent inlining so RemoveUnusedPolyfills can always recognize this\\n * call.\\n */\\n$jscomp.polyfill = function(target, polyfill, fromLang, toLang) {\\n if (!polyfill) return;\\n var obj = $jscomp.global;\\n var split = target.split(\'.\');\\n for (var i = 0; i < split.len'; +a.a+='gth - 1; i++) {\\n var key = split[i];\\n if (!(key in obj)) obj[key] = {}; // Might want to be defineProperty.\\n obj = obj[key];\\n }\\n var property = split[split.length - 1];\\n var orig = obj[property];\\n var impl = polyfill(orig);\\n if (impl == orig || impl == null) return;\\n $jscomp.defineProperty(\\n obj, property, {configurable: true, writable: true, value: impl});\\n};\\n","js/util/reflectobject.js":"/*\\n * Copyright 2017 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n\'require base\';\\n\\n/**\\n * Definition for object reflection.\\n *\\n * Internal compiler version of closure library goog.reflect.object.\\n *\\n * Use this if you have an object literal whose keys need to have the same names\\n * as the properties of some class even after they are renamed by the compiler.\\n *\\n * @param {?Object} type class, interface, or record\\n * @param {T} object Object literal whose properties must be renamed\\n * consistently with type\\n * @return {T} The object literal.\\n * @template T\\n */\\n$jscomp.reflectObject = function(type, object) {\\n return object;\\n};\\n\\n/**\\n * Definition for object property reflection.\\n *\\n * Internal compiler version of closure library goog.reflect.objectProperty.\\n *\\n * Use this if you have a string that needs renamed as if it were an unquoted\\n * property of a class.\\n *\\n * @param {string} propName\\n * @param {?Object} type class, interface, or record\\n * @return {string}\\n */\\n$jscomp.reflectProperty = function(propName, type) {\\n return propName;\\n};\\n","js/util/stringpadding.js":"/*\\n * Copyright 2017 The Closure Compiler Authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n\'require es6/string/repeat\';\\n\\n/**\\n * Repeats the given string as necessary to reach the given length,\\n * truncating any extra characters.\\n * @param {string|undefined} padString\\n * @param {number} padLength\\n * @return {string}\\n */\\n$jscomp.stringPadding = function(padString, padLength) {\\n var padding = padString !== undefined ? String(padString) : \' \';\\n if (!(padLength > 0) || !padding) return \'\';\\n var repeats = Math.ceil(padLength / padding.length);\\n return padding.repeat(repeats).substring(0, padLength);\\n};\\n","js/polyfills.txt":"Array.from es6 es3 es6/array/from\\nArray.of es6 es3 es6/array/of\\nArray.prototype.copyWithin es6 es3 es6/array/copywithin\\nArray.prototype.entries es6 es3 es6/array/entries\\nArray.prototype.fill es6 es3 es6/array/fill\\nArray.prototype.find es6 es3 es6/array/find\\nArray.prototype.findIndex es6 es3 es6/array/findindex\\nArray.prototype.flat es9 es5 es6/array/flat\\nArray.prototype.flatMap es9 es5 es6/array/flatmap\\nArray.prototype.includes es7 es3 es6/array/includes\\nArray.prototype.keys es6 es3 es6/array/keys\\nArray.prototype.values es8 es3 es6/array/values\\nMap es6 es3 es6/map\\nMath.acosh es6 es3 es6/math/acosh\\nMath.asinh es6 es3 es6/math/asinh\\nMath.atanh es6 es3 es6/math/atanh\\nMath.cbrt es6 es3 es6/math/cbrt\\nMath.clz32 es6 es3 es6/math/clz32\\nMath.cosh es6 es3 es6/math/cosh\\nMath.expm1 es6 es3 es6/math/expm1\\nMath.fround es6 es3 es6/math/fround\\nMath.hypot es6 es3 es6/math/hypot\\nMath.imul es6 es3 es6/math/imul\\nMath.log10 es6 es3 es6/math/log10\\nMath.log1p es6 es3 es6/math/log1p\\nMath.log2 es6 es3 es6/math/log2\\nMath.sign es6 es3 es6/math/sign\\nMath.sinh es6 es3 es6/math/sinh\\nMath.tanh es6 es3 es6/math/tanh\\nMath.trunc es6 es3 es6/math/trunc\\nNumber.EPSILON es6 es3 es6/number/constants\\nNumber.MAX_SAFE_INTEGER es6 es3 es6/number/constants\\nNumber.MIN_SAFE_INTEGER es6 es3 es6/number/constants\\nNumber.isFinite es6 es3 es6/number/isfinite\\nNumber.isInteger es6 es3 es6/number/isinteger\\nNumber.isNaN es6 es3 es6/number/isnan\\nNumber.isSafeInteger es6 es3 es6/number/issafeinteger\\nNumber.parseFloat es6 es3 es6/number/parsefloat\\nNumber.parseInt es6 es3 es6/number/parseint\\nObject.assign es6 es3 es6/object/assign\\nObject.entries es8 es3 es6/object/entries\\nObject.getOwnPropertyDescriptors es8 es5 es6/object/getownpropertydescriptors\\nObject.getOwnPropertySymbols es6 es5 es6/object/getownpropertysymbols\\nObject.is es6 es3 es6/object/is\\nObject.setPrototypeOf es6 es5 es6/object/setprototypeof\\nObject.values es8 es3 es6/object/values\\nPromise es6 es3 es6/promise/promise\\nPromise.prototype.finally es9 es3 es6/promise/finally\\nProxy es6 es6\\nReflect.apply es6 es3 es6/reflect/apply\\nReflect.construct es6 es3 es6/reflect/construct\\nReflect.defineProperty es6 es5 es6/reflect/defineproperty\\nReflect.deleteProperty es6 es3 es6/reflect/deleteproperty\\nReflect.get es6 es5 es6/reflect/get\\nReflect.getOwnPropertyDescriptor es6 es5 es6/reflect/getownpropertydescriptor\\nReflect.getPrototypeOf es6 es5 es6/reflect/getprototypeof\\nReflect.has es6 es3 es6/reflect/has\\nReflect.isExtensible es6 es3 es6/reflect/isextensible\\nReflect.ownKeys es6 es5 es6/reflect/ownkeys\\nReflect.preventExtensions es6 es3 es6/reflect/preventextensions\\nReflect.set es6 es5 es6/reflect/set\\nReflect.setPrototypeOf es6 es5 es6/reflect/setprototypeof\\nSet es6 es3 es6/set\\nString.fromCodePoint es6 es3 es6/string/fromcodepoint\\nString.prototype.codePointAt es6 es3 es6/string/codepointat\\nString.prototype.endsWith es6 es3 es6/string/endswith\\nString.prototype.includes es6 es3 es6/string/includes\\nString.prototype.normalize es6 es6\\nString.prototype.padEnd es8 es3 es6/string/padend\\nString.prototype.padStart es8 es3 es6/string/padstart\\nString.prototype.repeat es6 es3 es6/string/repeat\\nString.prototype.startsWith es6 es3 es6/string/startswith\\nString.raw es6 es6\\nWeakMap es6 es3 es6/weakmap\\nWeakSet es6 es3 es6/weakset\\n","parsing/ParserConfig.properties":"# Copyright 2009 The Closure Compiler Authors.\\n#\\n# Licensed under the Apache License, Version 2.0 (the \\"License\\");\\n# you may not use this file except in compliance with the License.\\n# You may obtain a copy of the License at\\n#\\n# http://www.apache.org/licenses/LICENSE-2.0\\n#\\n# Unless required by applicable law or agreed to in writing, software\\n# distributed under the License is distributed on an \\"AS IS\\" BASIS,\\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n# See the License for the specific language governing permissions and\\n# limitations under the License.\\n\\n# Configuration options for the Parser.\\n#\\n# Allows us to update the allowed JSDoc annotations independently of the\\n# Compiler binary, so we can add new JSDoc annotations to old binaries.\\n\\n# The version of the compiler that we\'re currently building.\\n# Should be formatted as:\\n# Version# (Revision XXX)\\ncompiler.version = ${compiler.version}\\n\\n# The datestamp of the compiler that we\'re currently building.\\ncompiler.date = ${compiler.date}\\n\\n# A comma-delimited list.\\n# Some of these are not used by JSCompiler, but appear in third-party JS code.\\n# http://usejsdoc.org/\\n# It\'s not strictly necessary for the natively-supported annotations to be\\n# listed here, but it\'s nice to have them all in one place.\\njsdoc.annotations =\\\\\\n addon,\\\\\\n alias,\\\\\\n animations,\\\\\\n appliesMixin,\\\\\\n augments,\\\\\\n author,\\\\\\n base,\\\\\\n borrows,\\\\\\n bug,\\\\\\n channel, \\\\\\n class,\\\\\\n classdesc,\\\\\\n closurePrimitive,\\\\\\n codepen,\\\\\\n config,\\\\\\n consistentIdGenerator,\\\\\\n constructor,\\\\\\n constructs,\\\\\\n copyright,\\\\\\n default,\\\\\\n delegate,\\\\\\n demo,\\\\\\n deprecated,\\\\\\n desc,\\\\\\n description,\\\\\\n dict,\\\\\\n docsNotRequired,\\\\\\n docs-private,\\\\\\n element,\\\\\\n enhance,\\\\\\n enhanceable,\\\\\\n enum,\\\\\\n event,\\\\\\n eventOf,\\\\\\n eventType,\\\\\\n example,\\\\\\n exception,\\\\\\n exec,\\\\\\n export,\\\\\\n exportDoc,\\\\\\n exportInterface,\\\\\\n expose,\\\\\\n externs,\\\\\\n field,\\\\\\n file,\\\\\\n fires,\\\\\\n function,\\\\\\n global,\\\\\\n hassoydelcall,\\\\\\n hassoydeltemplate,\\\\\\n hideconstructor, \\\\\\n id,\\\\\\n idGenerator,\\\\\\n ignore,\\\\\\n inner,\\\\\\n instance,\\\\\\n kind,\\\\\\n lends,\\\\\\n link,\\\\\\n meaning,\\\\\\n member,\\\\\\n memberOf,\\\\\\n memberof,\\\\\\n method,\\\\\\n methodOf,\\\\\\n mixes,\\\\\\n mixin,\\\\\\n modName,\\\\\\n moddedBy,\\\\\\n model,\\\\\\n modifies,\\\\\\n mods,\\\\\\n module,\\\\\\n multiElement,\\\\\\n name,\\\\\\n namespace,\\\\\\n ngInject,\\\\\\n ngdoc,\\\\\\n noalias,\\\\\\n nocompile,\\\\\\n package,\\\\\\n param,\\\\\\n parent,\\\\\\n pintomodule,\\\\\\n preserveTry,\\\\\\n priority,\\\\\\n private,\\\\\\n property,\\\\\\n propertyOf,\\\\\\n protected,\\\\\\n provideGoog,\\\\\\n pseudoElement,\\\\\\n public,\\\\\\n readonly,\\\\\\n requirecss,\\\\\\n requires,\\\\\\n requireExtern,\\\\\\n restrict,\\\\\\n returns,\\\\\\n scope,\\\\\\n see,\\\\\\n since,\\\\\\n stableIdGenerator,\\\\\\n static,\\\\\\n struct,\\\\\\n summary,\\\\\\n supported,\\\\\\n this,\\\\\\n throws,\\\\\\n todo,\\\\\\n transaction,\\\\\\n tutorial,\\\\\\n type,\\\\\\n typedef,\\\\\\n typeSummary,\\\\\\n url,\\\\\\n usage,\\\\\\n version,\\\\\\n virtual,\\\\\\n visibility,\\\\\\n wizSupportsSymbolicLookup,\\\\\\n wizaction,\\\\\\n wizmodule\\n\\n# Comma-delimited list of valid suppressions.\\n# This should be a subset of the list of DiagnosticGroups.\\njsdoc.suppressions =\\\\\\n accessControls,\\\\\\n ambiguousFunctionDecl,\\\\\\n checkDebuggerStatement,\\\\\\n checkEventfulObjectDisposal,\\\\\\n checkPrototypalTypes,\\\\\\n checkRegExp,\\\\\\n checkStaticOverrides,\\\\\\n checkTypes,\\\\\\n checkVars,\\\\\\n closureDepMethodUsageChecks,\\\\\\n const,\\\\\\n constantProperty,\\\\\\n deprecated,\\\\\\n duplicate,\\\\\\n es5Strict,\\\\\\n externsValidation,\\\\\\n extraProvide,\\\\\\n extraRequire,\\\\\\n fileoverviewTags,\\\\\\n globalThis,\\\\\\n googModuleExportNotAStatement,\\\\\\n invalidCasts,\\\\\\n legacyGoogScopeRequire,\\\\\\n lateProvide,\\\\\\n lintChecks,\\\\\\n messageConventions,\\\\\\n misplacedTypeAnnotation,\\\\\\n missingOverride, \\\\\\n missingPolyfill, \\\\\\n missingProperties,\\\\\\n missingProvide,\\\\\\n missingRequire,\\\\\\n missingReturn,\\\\\\n missingSourcesWarnings,\\\\\\n moduleLoad,\\\\\\n newCheckTypes,\\\\\\n newCheckTypesAllChecks,\\\\\\n nonStandardJsDocs,\\\\\\n polymer,\\\\\\n reportUnknownTypes,\\\\\\n strictCheckTypes,\\\\\\n strictMissingProperties,\\\\\\n strictModuleDepCheck,\\\\\\n strictPrimitiveOperators,\\\\\\n suspiciousCode,\\\\\\n transitionalSuspiciousCodeWarnings,\\\\\\n undefinedNames,\\\\\\n undefinedVars,\\\\\\n underscore,\\\\\\n unknownDefines,\\\\\\n unusedLocalVariables,\\\\\\n unusedPrivateMembers,\\\\\\n uselessCode,\\\\\\n visibility,\\\\\\n with\\n\\n# A comma-delimited list of valid closure primitive ids.\\n# This correspond to the ClosurePrimitive enum once normalized (see ClosurePrimitive.fromStringId)\\njsdoc.primitives =\\\\\\n asserts.fail,\\\\\\n\\n# A comma-delimited list of reserved words that we should not rename variables\\n# to. Used when an extension is released that steps on globals.\\n# This prevents the compiler from renaming variables to these names, but not\\n# from allowing externs for these names.\\n#\\n# i,j - common loop variables often overwritten by browser extensions\\n# s \u2013 commonly defined by browser extensions when injecting scripts.\\n# $j,$ - common jquery aliases often overwritten by browser extensions\\n# o - overwritten by Norton Identity Protection\'s Chrome extension.\\n# ga,_gaq - global variable names used by Google Analytics.\\n# sun - May cause issues in older browsers with remnants of Java support.\\n# XR - extern added by the webXR platform API.\\n# TODO(tbreisacher): Remove \'ga\' and \'_gaq\' if/when we enable\\n# --isolation_mode=IIFE by default.\\ncompiler.reserved.vars = i,j,s,$,$j,o,ga,_gaq,sun,XR\\n","rhino/Messages.properties":"#\\n# Default JavaScript messages file.\\n#\\n# ***** BEGIN LICENSE BLOCK *****\\n# Version: MPL 1.1/GPL 2.0\\n#\\n# The contents of this file are subject to the Mozilla Public License Version\\n# 1.1 (the \\"License\\"); you may not use this file except in compliance with\\n# the License. You may obtain a copy of the License at\\n# http://www.mozilla.org/MPL/\\n#\\n# Software distributed under the License is distributed on an \\"AS IS\\" basis,\\n# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\\n# for the specific language governing rights and limitations under the\\n# License.\\n#\\n# The Original Code is Rhino code, released\\n# May 6, 1999.\\n#\\n# The Initial Developer of the Original Code is\\n# Netscape Communications Corporation.\\n# Portions created by the Initial Developer are Copyright (C) 1997-1999\\n# the Initial Developer. All Rights Reserved.\\n#\\n# Contributor(s):\\n# Norris Boyd\\n# Bob Jervis\\n# Pascal-Louis Perez\\n#\\n# Alternatively, the contents of this file may be used under the terms of\\n# the GNU General Public License Version 2 or later (the \\"GPL\\"), in which\\n# case the provisions of the GPL are applicable instead of those above. If\\n# you wish to allow use of your version of this file only under the terms of\\n# the GPL and not to allow others to use your version of this file under the\\n# MPL, indicate your decision by deleting the provisions above and replacing\\n# them with the notice and other provisions required by the GPL. If you do\\n# not delete the provisions above, a recipient may use your version of this\\n# file under either the MPL or the GPL.\\n#\\n# ***** END LICENSE BLOCK *****\\n\\n# This is replaced during jar assembly from property string\\n# and should not be translated\\nimplementation.version = @IMPLEMENTATION.VERSION@\\n\\n#\\n# To add JavaScript error messages for a particular locale, create a\\n# new Messages_[locale].properties file, where [locale] is the Java\\n# string abbreviation for that locale. For example, JavaScript\\n# messages for the Polish locale should be located in\\n# Messages_pl.properties, and messages for the Italian Swiss locale\\n# should be located in Messages_it_CH.properties. Message properties\\n# files should be accessible through the classpath under\\n# org.mozilla.javascript.resources\\n#\\n# See:\\n# java.util.ResourceBundle\\n# java.text.MessageFormat\\n#\\n\\n# SomeJavaClassWhereUsed\\n\\n# Codegen\\nmsg.dup.parms =\\\\\\n Duplicate parameter name \\"{0}\\".\\n\\nmsg.unexpected.eof =\\\\\\n Unexpected end of file\\n\\nmsg.extra.trailing.comma =\\\\\\n Trailing comma is not legal in an ECMA-262 object initializer\\n\\nmsg.end.annotation.expected =\\\\\\n expected end of line or comment.\\n\\nmsg.bad.jsdoc.tag =\\\\\\n illegal use of unknown JSDoc tag \\"{0}\\"; ignoring it\\n\\nmsg.bad.fileoverview.visibility.annotation =\\\\\\n {0} visibility not allowed in @fileoverview block\\n\\nmsg.missing.variable.name =\\\\\\n expecting a variable name in a @param tag.\\n\\nmsg.dup.variable.name =\\\\\\n duplicate variable name \\"{0}\\"\\n\\nmsg.invalid.variable.name =\\\\\\n invalid param name \\"{0}\\"\\n\\nmsg.jsdoc.incompat.type =\\\\\\n type annotation incompatible with other annotations.\\n\\nmsg.jsdoc.type.syntax =\\\\\\n type not recognized due to syntax error.\\n\\nmsg.jsdoc.override =\\\\\\n extra @override/@inheritDoc tag.\\n\\nmsg.jsdoc.final =\\\\\\n extra @final tag.\\n\\nmsg.jsdoc.extra.visibility=\\\\\\n extra visibility tag\\n\\nmsg.jsdoc.idgen.duplicate =\\\\\\n extra @idGenerator tag\\n\\nmsg.jsdoc.idgen.bad =\\\\\\n malformed @idGenerator tag\\n\\nmsg.jsdoc.wizaction =\\\\\\n extra @wizaction tag\\n\\nmsg.jsdoc.idgen.unknown =\\\\\\n unknown @idGenerator parameter: {0}\\n\\nmsg.js'; +a.a+="doc.hidden =\\\\\\n extra @hidden tag\\n\\nmsg.jsdoc.consistidgen =\\\\\\n extra @consistentIdGenerator tag\\n\\nmsg.jsdoc.const =\\\\\\n conflicting @const tag\\n\\nmsg.jsdoc.desc.extra =\\\\\\n extra @desc tag\\n\\nmsg.jsdoc.meaning.extra =\\\\\\n extra @meaning tag\\n\\nmsg.jsdoc.fileoverview.extra =\\\\\\n extra @fileoverview tag\\n\\nmsg.jsdoc.lends.incompatible =\\\\\\n @lends tag incompatible with other annotations.\\n\\nmsg.jsdoc.lends.missing =\\\\\\n missing object name in @lends tag.\\n\\nmsg.jsdoc.closurePrimitive.extra =\\\\\\n conflicting @closurePrimitive tag\\n\\nmsg.jsdoc.closurePrimitive.invalid =\\\\\\n invalid id in @closurePrimitive tag.\\n\\nmsg.jsdoc.closurePrimitive.missing =\\\\\\n missing id in @closurePrimitive tag.\\n\\nmsg.jsdoc.preserve.nobuilder =\\\\\\n @preserve or @license annotation without file to associate it with\\n\\nmsg.jsdoc.missing.lc =\\\\\\n missing opening '{'\\n\\nmsg.jsdoc.missing.lp =\\\\\\n missing opening (\\n\\nmsg.jsdoc.missing.braces =\\\\\\n Type annotations should have curly braces.\\n\\nmsg.jsdoc.missing.rc =\\\\\\n expected closing }\\n\\nmsg.jsdoc.missing.rp =\\\\\\n missing closing )\\n\\nmsg.jsdoc.missing.gt =\\\\\\n missing closing >\\n\\nmsg.jsdoc.missing.rb =\\\\\\n missing closing ]\\n\\nmsg.jsdoc.missing.colon =\\\\\\n expecting colon after this\\n\\nmsg.jsdoc.function.this =\\\\\\n expecting this but {0} found\\n\\nmsg.jsdoc.function.newnotobject =\\\\\\n constructed type must be an object type\\n\\nmsg.jsdoc.function.varargs =\\\\\\n variable length argument must be last.\\n\\nmsg.jsdoc.type.union =\\\\\\n union type element with bad syntax\\n\\nmsg.jsdoc.type.record.duplicate =\\\\\\n Duplicate record field {0}.\\n\\nmsg.jsdoc.enum =\\\\\\n conflicting @enum tag\\n\\nmsg.jsdoc.constructor =\\\\\\n conflicting @constructor tag\\n\\nmsg.jsdoc.deprecated =\\\\\\n extra @deprecated tag\\n\\nmsg.jsdoc.interface =\\\\\\n extra @interface tag\\n\\nmsg.jsdoc.interface.constructor =\\\\\\n cannot be both an interface and a constructor.\\n\\nmsg.jsdoc.record =\\\\\\n conflicting @record tag.\\n\\nmsg.jsdoc.implements.duplicate =\\\\\\n duplicate @implements tag.\\n\\nmsg.jsdoc.implements.extraqualifier =\\\\\\n @implements/@extends requires a bare interface/record name without ! or ?.\\n\\nmsg.jsdoc.nosideeffects =\\\\\\n conflicting @nosideeffects tag\\n\\nmsg.jsdoc.implicitcast =\\\\\\n extra @implicitCast tag.\\n\\nmsg.jsdoc.this =\\\\\\n conflicting @this tag\\n\\nmsg.jsdoc.this.object =\\\\\\n @this must specify an object type\\n\\nmsg.jsdoc.type =\\\\\\n conflicting @type tag\\n\\nmsg.jsdoc.define =\\\\\\n conflicting @define tag\\n\\nmsg.jsdoc.define.badtype =\\\\\\n @define tag only permits literal types\\n\\nmsg.jsdoc.extends =\\\\\\n conflicting @extends tag\\n\\nmsg.jsdoc.extends.duplicate =\\\\\\n duplicate @extends tag\\n\\nmsg.jsdoc.export =\\\\\\n extra @export tag\\n\\nmsg.jsdoc.expose =\\\\\\n extra @expose tag\\n\\nmsg.jsdoc.externs =\\\\\\n extra @externs tag\\n\\nmsg.jsdoc.typesummary =\\\\\\n extra @typeSummary tag\\n\\nmsg.jsdoc.nocompile =\\\\\\n extra @nocompile tag\\n\\nmsg.jsdoc.nocollapse =\\\\\\n extra @nocollapse tag\\n\\nmsg.jsdoc.noinline =\\\\\\n extra @noinline tag\\n\\nmsg.jsdoc.seemissing =\\\\\\n @see tag missing description\\n\\nmsg.jsdoc.authormissing =\\\\\\n @author tag missing author\\n\\nmsg.jsdoc.versionmissing =\\\\\\n @version tag missing version information\\n\\nmsg.jsdoc.extraversion =\\\\\\n conflicting @version tag\\n\\nmsg.jsdoc.suppress =\\\\\\n malformed @suppress tag\\n\\nmsg.jsdoc.suppress.unknown =\\\\\\n unknown @suppress parameter: {0}\\n\\nmsg.jsdoc.modifies =\\\\\\n malformed @modifies tag\\n\\nmsg.jsdoc.modifies.duplicate =\\\\\\n conflicting @modifies tag\\n\\nmsg.jsdoc.modifies.unknown =\\\\\\n unknown @modifies parameter: {0}\\n\\nmsg.jsdoc.polymerBehavior.extra =\\\\\\n extra @polymerBehavior tag\\n\\nmsg.jsdoc.polymer.extra =\\\\\\n extra @polymer tag\\n\\nmsg.jsdoc.customElement.extra =\\\\\\n extra @customElement tag\\n\\nmsg.jsdoc.mixinClass.extra =\\\\\\n extra @mixinClass tag\\n\\nmsg.jsdoc.mixinFunction.extra =\\\\\\n extra @mixinFunction tag\\n\\nmsg.jsdoc.stableidgen =\\\\\\n extra @stableIdGenerator tag\\n\\nmsg.jsdoc.templatemissing =\\\\\\n @template tag missing type name.\\n\\nmsg.jsdoc.template.name.declared.twice =\\\\\\n Type name(s) for @template annotation declared twice.\\n\\nmsg.jsdoc.template.invalid.type.name =\\\\\\n Invalid type name(s) for @template annotation.\\n\\nmsg.jsdoc.disposeparameter.missing =\\\\\\n @disposes tag missing parameter name.\\n\\nmsg.jsdoc.disposeparameter.error =\\\\\\n @disposes parameter unknown or parameter specified multiple times.\\n\\nmsg.jsdoc.nginject.extra =\\\\\\n extra @ngInject tag\\n\\nmsg.no.type.name =\\\\\\n expecting a type name.\\n\\nmsg.jsdoc.typetransformation.missing.delimiter =\\\\\\n Expected end delimiter for a type transformation.\\n\\nmsg.jsdoc.typetransformation.with.multiple.names =\\\\\\n Type transformation must be associated to a single type name.\\n\\nmsg.jsdoc.typetransformation.expression.missing =\\\\\\n Missing type transformation expression.\\n\\nmsg.jsdoc.typetransformation.invalid =\\\\\\n Invalid {0}\\n\\nmsg.jsdoc.typetransformation.invalid.expression =\\\\\\n Invalid {0} expression\\n\\nmsg.jsdoc.typetransformation.missing.param =\\\\\\n Missing parameter in {0}\\n\\nmsg.jsdoc.typetransformation.extra.param =\\\\\\n Found extra parameter in {0}\\n\\nmsg.jsdoc.typetransformation.invalid.inside =\\\\\\n Invalid expression inside {0}\\n\"}\n"; +return a.a}var WKc="object",XKc="boolean",YKc="number",ZKc="function",$Kc="string",_Kc=2147483647,aLc={337:1,3:1},bLc="use Optional.orNull() instead of Optional.or(null)",cLc={32:1},dLc=65535,eLc={77:1,3:1},fLc="0123456789ABCDEF",gLc={137:1,52:1,49:1},hLc="[A-Z\\d]",iLc=", ",jLc="null",kLc=1048,lLc={15:1,21:1},mLc={3:1,4:1},nLc=1066,oLc={15:1,21:1,54:1},pLc="%s (%s) must not be negative",qLc="index",rLc="negative size: ",sLc={3:1,11:1,4:1},tLc={52:1,142:1,3:1,20:1,12:1,49:1},uLc={15:1},vLc={105:1, +32:1},wLc={105:1,32:1,153:1},xLc={100:1,3:1},yLc={51:1},zLc=1047,ALc={15:1,21:1,35:1},BLc={63:1},CLc="no calls to next() since the last call to remove()",DLc={15:1,21:1,54:1,69:1},ELc={32:1,153:1},FLc={15:1,21:1,35:1,154:1},GLc={15:1,21:1,316:1,35:1,154:1},HLc="occurrences cannot be negative: %s",ILc={176:1,50:1},JLc=16384,KLc={94:1},LLc={l:4194303,m:4194303,h:524287},MLc="unreachable",NLc={66:1,3:1,15:1,21:1},OLc={66:1,113:1,3:1,15:1,21:1,54:1,69:1},PLc={3:1,4:1,93:1},QLc={134:1,3:1,51:1},RLc={66:1, +135:1,3:1,15:1,21:1,35:1},SLc={50:1},TLc={647:1,3:1},ULc={220:1,3:1},VLc=461845907,WLc=-862048943,XLc="expectedSize",YLc={3:1,51:1},ZLc="value already present: %s",$Lc=1073741824,_Lc={183:1,63:1},aMc="expectedValuesPerKey",bMc="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",cMc={430:1,3:1},dMc="Unrecognized character: 0x",eMc="__noinit__",fMc="stack",gMc="\n",hMc=-2147483648,iMc={129:1,3:1},jMc={1041:1},kMc=65536,lMc=1114111,mMc=1023,nMc={3:1,38:1,25:1,36:1},oMc="String",pMc="anonymous", +qMc="fnStack",rMc="Unknown",sMc=4194303,tMc=1048575,uMc=524288,vMc=4194304,wMc=17592186044416,xMc=4095,yMc=-17592186044416,zMc="ERROR",AMc="WARNING",BMc="arguments",CMc="SUPER",DMc="super",EMc="THIS",FMc="this",GMc="OFF",HMc="Invalid name '%s'. Did you mean to use NodeUtil.newQName?",IMc={17:1,3:1,4:1},JMc="$jscomp.makeIterator",KMc="$jscomp.asyncExecutePromiseGeneratorFunction",LMc="constructor",MMc="Object.assign",NMc="Object",OMc="Expected 0 children, but was ",PMc="Expected 2 children, but was ", +QMc="Expected ",RMc=" children, but was ",SMc="Invalid child for ",TMc=" node",UMc="Expected 1 children, but was ",VMc="Expected child count in [1, 2], but was ",WMc="Expected non-empty string.",XMc="Expected non-null string.",YMc="Expected unnamed function expression.",ZMc="Expected child count in [2, 3], but was ",$Mc={45:1},_Mc=1000003,aNc="prototype",bNc="Missing required properties:",cNc="%20",dNc="%5B",eNc="%5D",fNc="%3C",gNc="%3E",hNc="JSC_LATE_PROVIDE_ERROR",iNc='Required namespace "{0}" not provided yet.', +jNc="Module imports must be constant. Please use ''const'' instead of ''let''.",kNc="goog",lNc="require",mNc="module",nNc="get",oNc="forwardDeclare",pNc="requireType",qNc=1081,rNc={22:1},sNc="cannot reference {2} because of a missing module dependency\ndefined in module {1}, referenced from module {0}",tNc="missing require: ''{0}''",uNc={3:1,4:1,26:1},vNc="global",wNc={3:1},xNc="Array.isArray",yNc="goog.addSingletonGetter",zNc="goog$addSingletonGetter",ANc="use strict",BNc="loadModule",CNc="provide", +DNc="finally",ENc="return",FNc="Unexpected Node subclass.",GNc="async",HNc="...",INc="export",JNc="default",KNc="from",LNc="import",MNc="class",NNc="extends",ONc="implements",PNc="while",QNc="new.target",RNc="yield",SNc="false",TNc="true",UNc="continue",VNc="Unexpected token type. Should be LABEL_NAME.",WNc="debugger",XNc="void",YNc="interface",ZNc="enum",$Nc="type",_Nc="async function",aOc="Expected qualified name, found: %s",bOc="$jscomp.inherits",cOc="$jscomp$inherits",dOc="No inputs. Did you call init() or initModules()?", +eOc="generateReport",fOc=".js",gOc="$weak$",hOc="module$",iOc="toSource",jOc="parseInputs",kOc="es6",lOc="runTranspileOnlyPasses",mOc="runWhitespaceOnlyPasses",nOc={47:1},oOc="goog.module.declareNamespace",pOc="goog.declareModuleId",qOc="NONE",rOc="./",sOc="checkTypes",tOc="collapseObjectLiterals",uOc="inlineVariables",vOc="removeUnusedClassProperties",wOc="removeUnusedPrototypeProperties",xOc="ECMASCRIPT3",yOc="ECMASCRIPT5",zOc="ECMASCRIPT_2018",AOc="ECMASCRIPT_2019",BOc="UNSUPPORTED",COc="ALL", +DOc="MODULE_EXPORT",EOc={167:1,3:1},FOc={3:1,101:1},GOc="{0}",HOc="reportUnknownTypes",IOc="analyzerChecks",JOc="analyzerChecksInternal",KOc="oldReportUnknownTypes",LOc="newCheckTypes",MOc="newCheckTypesCompatibility",NOc="newCheckTypesExtraChecks",OOc="missingSourcesWarnings",POc="strictMissingProperties",QOc="strictPrimitiveOperators",ROc="strictCheckTypes",SOc="deprecated",TOc="fileoverviewTags",UOc="missingProperties",VOc="newCheckTypesAllChecks",WOc="checkEventfulObjectDisposal",XOc="const", +YOc="duplicate",ZOc="missingRequire",$Oc="polymerBehavior",_Oc="duplicateZipContents",aPc={158:1,3:1,20:1,12:1},bPc=134217728,cPc="Only calls to super or to a method of super are supported.",dPc="This should never happen. Did Es6SuperCheck fail to run?",ePc="call",fPc={45:1,22:1},gPc={52:1,49:1},hPc="apply",iPc="Error",jPc="RangeError",kPc="Array",lPc="Boolean",mPc="Float32Array",nPc="Function",oPc="Number",pPc="Promise",qPc="RegExp",rPc="Symbol",sPc="$jscomp$tmp$error",tPc="message",uPc="$jscomp$super$this", +vPc="undefined",wPc="next",xPc={29:1,3:1,4:1},yPc="value",zPc="es6/symbol",APc="es6/util/",BPc="makeIterator",CPc="util/global",DPc="ES5 getters/setters (consider using --language_out=ES5)",EPc="$jscomp$this",FPc="$jscomp$arguments",GPc="enumerable",HPc=-536870912,IPc=2097152,JPc=8388608,KPc="$jscomp.global.Object.defineProperties",LPc="$jscomp$destructuring$var",MPc="$jscomp$generator$function",NPc="$jscomp",OPc="$jscomp$generator$context",PPc="jumpTo",QPc="nextAddress",RPc="yieldResult",SPc="$$exports", +TPc="$$require",UPc="$$module",VPc="concat",WPc=536870912,XPc="$jscomp$restParams",YPc="$jscomp$restIndex",ZPc="JSC_CANNOT_CONVERT",$Pc="Math",_Pc="$jscomp$",aQc={22:1,317:1},bQc="goog.provide",cQc="goog.module",dQc={1039:1},eQc=1610612736,fQc=".prototype",gQc=".prototype.",hQc=67108864,iQc="CLASS",jQc="FUNCTION",kQc="SUBCLASSING_GET",lQc="expectedKeys",mQc="java.lang.Integer",nQc="java/lang/Integer.impl.java.js",oQc="java.lang.Float",pQc="java/lang/Float.impl.java.js",qQc="goog.math.Long",rQc="closure/goog/math/long.js", +sQc=32768,tQc=268435456,uQc="throws",vQc="@template ",wQc={1044:1},xQc="Infinity",yQc="typeof",zQc="instanceof",AQc="Normalize constraints violated:\n",BQc="Duplicate VAR declaration",CQc=1048576,DQc="Unexpected const change.\n name: ",EQc={101:1},FQc="peepholeOptimizations",GQc="removeUnreachableCode",HQc="JSC_CLOSURE_CALL_CANNOT_BE_ALIASED_ERROR",IQc="module.exports",JQc="__webpack_require__.e",KQc="Promise.all",LQc="$jscomp$async$this",MQc="$jscomp$async$arguments",NQc="$jscomp.AsyncGeneratorWrapper$ActionRecord", +OQc="Cannot prepend declarations to root scope",PQc="$jscomp$asyncIter$this",QQc="$jscomp$asyncIter$arguments",RQc="$jscomp$asyncIter$super$get$",SQc="$jscomp.AsyncGeneratorWrapper$ActionEnum.AWAIT_VALUE",TQc="$jscomp$unused$catch",UQc="$jscomp$browser$blacklisted",VQc="/package.json",WQc="$jscomp.polyfill",XQc="Parse error. {0}",YQc="Keywords and reserved words are not allowed as unquoted property names in older versions of JavaScript. If you are targeting newer versions of JavaScript, set the appropriate language_in option.", +ZQc="Can only have JSDoc or inline type annotations, not both",$Qc="Trailing comma is not legal in an ECMA-262 object initializer",_Qc="msg.bad.jsdoc.tag",aRc={285:1,286:1,3:1},bRc={185:1,3:1,20:1,12:1},cRc="\ufeff",dRc={189:1,1108:1,3:1},eRc={52:1,195:1,3:1,20:1,12:1,49:1},fRc={194:1,3:1,20:1,12:1},gRc="DEFAULT",hRc="{SyntheticVarsDeclar}",iRc="-beginning-",jRc="Property {0} never defined on {1}",kRc="Property {0} never defined on {1}. Did you mean {2}?",lRc="Expected a constructor; got %s",mRc= +" * ",nRc="../",oRc={190:1,3:1,20:1,12:1},pRc={183:1,264:1,3:1,20:1,12:1,63:1},qRc="node_modules/",rRc="es_next",sRc="symbol",tRc="CONSTRUCTOR",uRc="ENUM",vRc="EXTENDS",wRc="EXPORT",xRc="IMPLEMENTS",yRc="INTERFACE",zRc="PACKAGE",ARc="PRIVATE",BRc="PROTECTED",CRc="PUBLIC",DRc="RETURN",ERc="TYPE",FRc="package",GRc="private",HRc="protected",IRc="public",JRc="TYPESCRIPT",KRc="static",LRc="This language feature is not currently supported by the compiler: ",MRc="This language feature is only supported for ", +NRc=" mode or better: ",ORc="Invalid octal digit in octal literal.",PRc="Octal integer literals are not supported in strict mode.",QRc="unexpected: ",RRc="__missing_expression__",SRc="unique",TRc="consistent",URc="Bad type annotation. ",VRc=" See https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler for more information.",WRc="msg.jsdoc.incompat.type",XRc="msg.jsdoc.interface.constructor",YRc="msg.jsdoc.missing.rc",ZRc="msg.jsdoc.template.name.declared.twice", +$Rc="msg.jsdoc.extra.visibility",_Rc="msg.jsdoc.type.syntax",aSc="msg.jsdoc.missing.rp",bSc="msg.jsdoc.idgen.bad",cSc="msg.jsdoc.idgen.duplicate",dSc="msg.jsdoc.modifies",eSc="msg.jsdoc.suppress",fSc="STRING",gSc="ANNOTATION",hSc="com.google.javascript.jscomp.parsing.ParserConfig",iSc="Expected a call node, found %s",jSc="msg.jsdoc.typetransformation.invalid",kSc="msg.jsdoc.typetransformation.invalid.expression",lSc="type transformation",mSc="msg.jsdoc.typetransformation.invalid.inside",nSc="conditional", +oSc="map function",pSc="Expected a function node, found %s",qSc="msg.jsdoc.typetransformation.missing.param",rSc="msg.jsdoc.typetransformation.extra.param",sSc="map function body",tSc="template type operation",uSc="INSTANCEOF",vSc="unknown",wSc="NEW_TARGET",xSc="FOR_AWAIT_OF",ySc="DYNAMIC_IMPORT",zSc="AMBIENT_DECLARATION",ASc="CALL_SIGNATURE",BSc="INDEX_SIGNATURE",CSc="NAMESPACE_DECLARATION",DSc="OPTIONAL_PARAMETER",ESc="TYPE_ALIAS",FSc="CATCH",GSc="CONTINUE",HSc="DEBUGGER",ISc="delete",JSc="FINALLY", +KSc="SWITCH",LSc="TYPEOF",MSc="IMPORT",NSc="NULL",OSc="TRUE",PSc="FALSE",QSc="DECLARE",RSc="NAMESPACE",SSc="'%s' expected",TSc="const variables must have an initializer",USc="destructuring must have an initializer",VSc="No newline allowed before '=>'",WSc="invalid assignment target",XSc="invalid parenthesized expression",YSc="cannot use keyword '%s' here.",ZSc="await",$Sc="Unterminated template literal",_Sc="Invalid escape sequence",aTc="In some cases, '\x3c!--' and '--\x3e' are treated as a '//' for legacy reasons. Removing this from your code is safe for all browsers currently in use.", +bTc="Hex digit expected",cTc="Empty unicode escape",dTc="ARRAY_PATTERN",eTc="OBJECT_PATTERN",fTc="ARRAY_TYPE",gTc="RECORD_TYPE",hTc="UNION_TYPE",iTc="FUNCTION_TYPE",jTc="GENERIC_TYPE_LIST",kTc=4294967295,lTc="Bit index should be non-negative integer",mTc="String has leading or trailing whitespace",nTc="The new child node has next siblings.",oTc="The new child node has previous siblings.",pTc="The new child node already has a parent.",qTc="%s is not the parent of %s",rTc="Template Literal String node not created with Node.newTemplateLitString", +sTc=" is not a template literal string node",tTc="Number node not created with Node.newNumber",uTc="String node not created with Node.newString",vTc=" is not a string node",wTc="GENERIC_TYPE",xTc="com.google.javascript.rhino.Messages",yTc={23:1,3:1},zTc={23:1,48:1,3:1},ATc={23:1,48:1,76:1,3:1},BTc={128:1,23:1,48:1,76:1,3:1},CTc="Type names cannot contain template annotations.",DTc="VALUE",ETc="Thenable",FTc={23:1,48:1,138:1,3:1},GTc={164:1,3:1,20:1,12:1},HTc={3:1,80:1,36:1},ITc='For input string: "', +JTc={20:1,192:1},KTc={3:1,15:1,21:1,54:1,69:1},LTc=16777619,MTc={3:1,15:1,21:1,35:1},NTc={3:1,20:1,12:1,148:1},OTc={3:1,4:1,1111:1},PTc={1036:1,1040:1},QTc="Invalid UTF8 sequence",RTc=", length: ",STc="fromIndex: ",TTc=", toIndex: ";var _,GZ,BZ,eZ=-1;$wnd.goog=$wnd.goog||{};$wnd.goog.global=$wnd.goog.global||$wnd;HZ();IZ(1,null,{},xb);_.yd=function yb(a){return wb(this,a)};_.zd=function Ab(){return this.Fj};_.Ad=function Cb(){return NKc(this)};_.Bd=function Eb(){var a;return frc(Bb(this))+"@"+(a= +Db(this)>>>0,a.toString(16))};_.equals=function(a){return this.yd(a)};_.hashCode=function(){return this.Ad()};_.toString=function(){return this.Bd()};var jF,kF,lF;IZ(449,1,{},hrc);_.kj=function irc(a){var b;b=new hrc;b.i=4;a>1?b.c=prc(this,a-1):b.c=this;return b};_.lj=function orc(){drc(this);return this.b};_.ug=function qrc(){return frc(this)};_.mj=function trc(){return grc(this)};_.nj=function vrc(){return(this.i&4)!=0};_.oj=function wrc(){return(this.i&1)!=0};_.Bd=function zrc(){return((this.i& +2)!=0?"interface ":(this.i&1)!=0?"":"class ")+(drc(this),this.p)};_.i=0;_.k=0;var crc=1;var iW=krc(1);var VV=krc(449);IZ(337,1,aLc);var QF=krc(337);IZ(724,337,aLc,Hb);_.yd=function Ib(a){return a===this};_.Cd=function Jb(){throw gZ(new Irc("Optional.get() cannot be called on an absent value"));};_.Ad=function Kb(){return 2040732332};_.Dd=function Lb(){return false};_.Ed=function Mb(a){return Sd(a)};_.Fd=function Nb(a){return Td(a,bLc)};_.Bd=function Ob(){return"Optional.absent()"};var Fb;var AF=krc(724); +IZ(834,1,cLc);_.Hd=function Tb(a){lEc(this,a)};_.Id=function Ub(){return Pb(this)};_.Jd=function Vb(){return Qb(this)};_.Kd=function Wb(){Rb()};_.i=1;var BF=krc(834);IZ(137,1,gLc);_.Ld=function bc(a){return this.Md(a.a)};_.yd=function cc(a){return this===a};_.Nd=function ec(a){return this.Md(a.a)};_.Bd=function fc(){var a;return frc(this.Fj)+"@"+(a=Db(this)>>>0,a.toString(16))};var LF=krc(137);IZ(832,137,gLc,gc);_.Md=function hc(a){return Bxc(this.a,a)>=0};_.Bd=function ic(){var a,b,c,d,e;e=new vtc('CharMatcher.anyOf("'); +for(b=this.a,c=0,d=b.length;c>>Gc)==a};var Fc,Gc=0;var KF=krc(830);IZ(84,1,{},Pc);var NF=krc(84);var cW=mrc();IZ(kLc,1,lLc);_.Zd=function dd(){return new tFc(this,0)};_.$d=function ed(){return new KJc(null,this.Zd())};_.Pd=function Xc(a){throw gZ(new ztc("Add not supported on this collection"));};_.Qd=function Yc(a){return Rc(this,a)};_.Rd=function Zc(){Tc(this)};_.Sd=function $c(a){return Sc(this,a,false)};_.Td=function _c(a){return Uc(this,a)};_.Ud=function ad(){return this.Yd()==0};_.Wd=function bd(a){return Sc(this, +a,true)};_.Xd=function cd(a){return Vc(this,a)};_._d=function fd(){return this.ae(zE(iW,mLc,1,this.Yd(),5,1))};_.ae=function gd(a){var b,c,d,e;e=this.Yd();a.lengthe&&(a[e]=null);return a};_.Bd=function hd(){return Wc(this)};var tW=krc(kLc);IZ(nLc,kLc,oLc);_.Zd=function vd(){return new tFc(this,16)};_.be=function ld(a,b){throw gZ(new ztc("Add not supported on this list"));};_.Pd=function md(a){this.be(this.Yd(),a);return true}; +_.Rd=function nd(){this.ge(0,this.Yd())};_.yd=function od(a){return jd(this,a)};_.Ad=function pd(){return Fyc(this)};_.Vd=function qd(){return new Guc(this)};_.de=function rd(){return this.ee(0)};_.ee=function sd(a){return new Muc(this,a)};_.fe=function td(a){throw gZ(new ztc("Remove not supported on this list"));};_.ge=function ud(a,b){var c,d;d=this.ee(a);for(c=a;c0};_.Jd=function Df(){return zf(this)};_.qe=function Ef(){if(this.c<=0)throw gZ(new jFc);return this.ce(--this.c)};_.c=0;_.d=0;var cG=krc(266);IZ(721,105,vLc);_.Id=function Hf(){return Ff(this)};_.Jd=function If(){var a;if(!Ff(this))throw gZ(new jFc);this.f=1;a=this.e;this.e=null;return a};_.f=1;var dG=krc(721);IZ(1087,1,{100:1});_.re=function Mf(){var a;return a=this.f,!a?this.f=this.te(): +a};_.yd=function Nf(a){return ly(this,a)};_.Ad=function Of(){return Db(this.re())};_.Ud=function Pf(){return this.Yd()==0};_.we=function Qf(){return Kf(this)};_.Bd=function Rf(){return NZ(this.re())};var BG=krc(1087);IZ(500,1087,xLc);_.Rd=function $f(){Sf(this)};_.se=function _f(a){return Tf(this,a)};_.te=function ag(){return new Wg(this,this.c)};_.ze=function bg(a){return this.ye()};_.ue=function cg(){return new Oh(this,this.c)};_.Ae=function dg(){return this.Be(this.ye())};_.ve=function eg(a){return Uf(this, +a)};_.xe=function gg(a){return Wf(this,a)};_.Yd=function hg(){return this.d};_.Be=function ig(a){return Cyc(),new Fzc(a)};_.Ce=function jg(a,b){return new di(this,a,b,null)};_.d=0;var tG=krc(500);IZ(873,500,xLc);_.ye=function lg(){return this.a.Cd()};_.Ae=function mg(){return Cyc(),Cyc(),Ayc};_.ve=function og(a){return Uf(this,a)};_.xe=function qg(a){return Wf(this,a)};_.re=function kg(){var a;return a=this.f,!a?this.f=new Wg(this,this.c):a};_.yd=function ng(a){return ly(this,a)};_.De=function pg(a){return Uf(this, +a)};_.Ee=function rg(a){return Wf(this,a)};_.Be=function sg(a){return Lyc(a)};_.Ce=function tg(a,b){return Yf(this,a,b,null)};var eG=krc(873);IZ(1064,1,yLc);_.Rd=function Bg(){this.He().Rd()};_.Fe=function Cg(a){return ug(this,a)};_.se=function Dg(a){return!!wg(this,a,false)};_.Ge=function Eg(a){var b,c,d;for(c=this.He().Vd();c.Id();){b=c.Jd();d=b.We();if(uF(a)===uF(d)||a!=null&&zb(a,d))return true}return false};_.yd=function Fg(a){return vg(this,a)};_.Ie=function Gg(a){return Hg(wg(this,a,false))}; +_.Ad=function Ig(){return Eyc(this.He())};_.Ud=function Jg(){return this.Yd()==0};_.we=function Kg(){return new Wuc(this)};_.Je=function Lg(a,b){throw gZ(new ztc("Put not supported on this map"));};_.Ke=function Mg(a){return Hg(wg(this,a,true))};_.Yd=function Ng(){return this.He().Yd()};_.Bd=function Og(){return zg(this)};_.Le=function Pg(){return new fvc(this)};var IW=krc(1064);IZ(1088,1064,yLc);_.He=function Qg(){var a;a=this.c;return!a?this.c=this.Me():a};_.we=function Rg(){var a;a=this.d;return!a? +this.d=new Hh(this):a};_.Le=function Sg(){var a;a=this.e;return!a?this.e=new Px(this):a};var ZI=krc(1088);IZ(245,1088,yLc,Wg);_.Ie=function _g(a){return Tg(this,a)};_.Ke=function dh(a){return Ug(this,a)};_.Rd=function Xg(){this.a==this.b.c?this.b.Rd():ou(new Ch(this))};_.se=function Yg(a){return Ax(this.a,a)};_.Ne=function Zg(){return new uh(this)};_.Me=function(){return this.Ne()};_.yd=function $g(a){return this===a||vg(this.a,a)};_.Ad=function ah(){return Eyc(this.a.He())};_.we=function bh(){return this.b.we()}; +_.Yd=function eh(){return this.a.Yd()};_.Bd=function fh(){return zg(this.a)};var iG=krc(245);IZ(zLc,kLc,ALc);_.Zd=function lh(){return new tFc(this,1)};_.yd=function ih(a){return gh(this,a)};_.Ad=function jh(){return Eyc(this)};_.Xd=function kh(a){return hh(this,a)};var PW=krc(zLc);IZ(1074,zLc,ALc);_.Xd=function mh(a){return tz(this,a)};var FJ=krc(1074);IZ(1075,1074,ALc);_.Rd=function oh(){this.Oe().Rd()};_.Sd=function ph(a){return nh(this,a)};_.Ud=function qh(){return this.Oe().Ud()};_.Wd=function rh(a){var b; +if(this.Sd(a)){b=a;return this.Oe().we().Wd(b.Ve())}return false};_.Xd=function sh(b){try{return tz(this,Sd(b))}catch(a){a=fZ(a);if(pF(a,28))return uz(this,b.Vd());else throw gZ(a);}};_.Yd=function th(){return this.Oe().Yd()};var UI=krc(1075);IZ(879,1075,ALc,uh);_.Sd=function vh(a){return Ql(this.a.a.He(),a)};_.Vd=function wh(){return new Ch(this.a)};_.Oe=function xh(){return this.a};_.Wd=function yh(a){var b;if(!Ql(this.a.a.He(),a))return false;b=a;Xf(this.a.b,b.Ve());return true};_.Zd=function zh(){return jl(this.a.a.He().Zd(), +new Ah(this.a))};var gG=krc(879);IZ(880,1,BLc,Ah);_.Od=function Bh(a){return Vg(this.a,a)};var fG=krc(880);IZ(507,1,cLc,Ch);_.Hd=function Dh(a){lEc(this,a)};_.Jd=function Fh(){var a;return a=this.b.Jd(),this.a=a.We(),Vg(this.c,a)};_.Id=function Eh(){return this.b.Id()};_.Kd=function Gh(){Yd(!!this.a,CLc);this.b.Kd();this.c.b.d-=this.a.Yd();this.a.Rd();this.a=null};var hG=krc(507);IZ(278,1074,ALc,Hh);_.Rd=function Ih(){this.b.Rd()};_.Sd=function Jh(a){return this.b.se(a)};_.Ud=function Kh(){return this.b.Ud()}; +_.Vd=function Lh(){return new Fx(this.b.He().Vd())};_.Wd=function Mh(a){if(this.b.se(a)){this.b.Ke(a);return true}return false};_.Yd=function Nh(){return this.b.Yd()};var XI=krc(278);IZ(297,278,ALc,Oh);_.Rd=function Ph(){var a;ou((a=this.b.He().Vd(),new Wh(this,a)))};_.Td=function Qh(a){return this.b.we().Td(a)};_.yd=function Rh(a){return this===a||zb(this.b.we(),a)};_.Ad=function Sh(){return Db(this.b.we())};_.Vd=function Th(){var a;return a=this.b.He().Vd(),new Wh(this,a)};_.Wd=function Uh(a){var b, +c;c=0;b=this.b.Ke(a);if(b){c=b.Yd();b.Rd();this.a.d-=c}return c>0};_.Zd=function Vh(){return this.b.we().Zd()};var kG=krc(297);IZ(508,1,cLc,Wh);_.Hd=function Xh(a){lEc(this,a)};_.Id=function Yh(){return this.c.Id()};_.Jd=function Zh(){this.a=this.c.Jd();return this.a.Ve()};_.Kd=function $h(){var a;Yd(!!this.a,CLc);a=this.a.We();this.c.Kd();this.b.a.d-=a.Yd();a.Rd();this.a=null};var jG=krc(508);IZ(296,kLc,lLc,di);_.Pd=function ei(a){var b,c;ai(this);c=this.d.Ud();b=this.d.Pd(a);if(b){++this.f.d;c&& +_h(this)}return b};_.Qd=function fi(a){var b,c,d;if(a.Ud())return false;d=(ai(this),this.d.Yd());b=this.d.Qd(a);if(b){c=this.d.Yd();this.f.d+=c-d;d==0&&_h(this)}return b};_.Rd=function gi(){var a;a=(ai(this),this.d.Yd());if(a==0)return;this.d.Rd();this.f.d-=a;bi(this)};_.Sd=function hi(a){ai(this);return this.d.Sd(a)};_.Td=function ii(a){ai(this);return this.d.Td(a)};_.yd=function ji(a){if(a===this)return true;ai(this);return zb(this.d,a)};_.Ad=function ki(){ai(this);return Db(this.d)};_.Vd=function li(){ai(this); +return new Di(this)};_.Wd=function mi(a){var b;ai(this);b=this.d.Wd(a);if(b){--this.f.d;bi(this)}return b};_.Xd=function ni(a){var b,c,d;if(a.Ud())return false;d=(ai(this),this.d.Yd());b=this.d.Xd(a);if(b){c=this.d.Yd();this.f.d+=c-d;bi(this)}return b};_.Yd=function oi(){return ci(this)};_.Zd=function pi(){return ai(this),this.d.Zd()};_.Bd=function qi(){ai(this);return NZ(this.d)};var nG=krc(296);IZ(505,296,oLc,ri);_.Zd=function xi(){return ai(this),this.d.Zd()};_.be=function si(a,b){var c;ai(this); +c=this.d.Ud();this.d.be(a,b);++this.a.d;c&&_h(this)};_.ce=function ti(a){ai(this);return this.d.ce(a)};_.de=function ui(){ai(this);return new Ji(this)};_.ee=function vi(a){ai(this);return new Ki(this,a)};_.fe=function wi(a){var b;ai(this);b=this.d.fe(a);--this.a.d;bi(this);return b};_.he=function yi(a,b){ai(this);return Yf(this.a,this.e,this.d.he(a,b),!this.b?this:this.b)};var pG=krc(505);IZ(878,505,DLc,zi);var lG=krc(878);IZ(378,1,cLc,Di);_.Hd=function Fi(a){lEc(this,a)};_.Id=function Gi(){Ci(this); +return this.b.Id()};_.Jd=function Hi(){Ci(this);return this.b.Jd()};_.Kd=function Ii(){Bi(this)};var mG=krc(378);IZ(506,378,ELc,Ji,Ki);_.Kd=function Oi(){Bi(this)};_.oe=function Li(a){var b;b=ci(this.a)==0;(Ci(this),this.b).oe(a);++this.a.a.d;b&&_h(this.a)};_.pe=function Mi(){return(Ci(this),this.b).pe()};_.qe=function Ni(){return(Ci(this),this.b).qe()};var oG=krc(506);IZ(504,296,FLc,Pi);_.Zd=function Si(){return ai(this),this.d.Zd()};_.Pe=function Qi(){return this.Qe().Pe()};_.Qe=function Ri(){return this.d}; +var sG=krc(504);IZ(877,504,GLc,Ti);_.Qe=function Ui(){return this.d};var qG=krc(877);IZ(503,296,ALc,Vi);_.Zd=function Xi(){return ai(this),this.d.Zd()};_.Xd=function Wi(a){var b,c,d;if(a.Ud())return false;d=(ai(this),this.d.Yd());b=tz(this.d,a);if(b){c=this.d.Yd();this.a.d+=c-d;bi(this)}return b};var rG=krc(503);IZ(1103,kLc,{1042:1,15:1,21:1});_.Zd=function jj(){var a;return a=Zi(this).Zd(),gl(a,new Ty,64|a.ef()&1296,VB(this.b))};_.Pd=function _i(a){return lj(this,a,1),true};_.Qd=function aj(a){return Sd(this), +Sd(a),pF(a,182)?Oy(this,a):!a.Ud()&&mu(this,a.Vd())};_.Sd=function bj(a){var b;return b=Bx(this.a,a),(!b?0:b.a)>0};_.He=function cj(){return Zi(this)};_.yd=function dj(a){return Py(this,a)};_.Ad=function ej(){return Db(this.He())};_.Ud=function fj(){return Zi(this).Ud()};_.Wd=function gj(a){return this.Re(a,1)>0};_.Re=function hj(a,b){throw gZ(new ytc);};_.Xd=function ij(a){return $i(this,a)};_.Bd=function kj(){return NZ(Zi(this))};var EG=krc(1103);IZ(1011,1103,{1042:1,3:1,15:1,21:1});_.Rd=function uj(){mj(this)}; +_.He=function vj(){return Zi(this)};_.Vd=function xj(){return new Rj(this)};_.Re=function yj(a,b){return rj(this,a,b)};_.Yd=function zj(){return VB(this.b)};_.b=0;var zG=krc(1011);IZ(1013,1,cLc,Aj);_.Hd=function Bj(a){lEc(this,a)};_.Id=function Cj(){return this.c.b};_.Jd=function Dj(){var a;a=wuc(this.c);this.b=a;return a.Ve()};_.Kd=function Ej(){Yd(!!this.b,CLc);this.a.b=vZ(this.a.b,$l(this.b.We(),0));xuc(this.c);this.b=null};var uG=krc(1013);IZ(1014,1,cLc,Fj);_.Hd=function Gj(a){lEc(this,a)};_.Jd= +function Ij(){var a;return a=wuc(this.c),this.b=a,new Oj(this,a)};_.Id=function Hj(){return this.c.b};_.Kd=function Jj(){Yd(!!this.b,CLc);this.a.b=vZ(this.a.b,$l(this.b.We(),0));xuc(this.c);this.b=null};var wG=krc(1014);var oJ=mrc();IZ(1091,1,{1043:1});_.yd=function Kj(a){var b;if(pF(a,1043)){b=a;return this.Se()==b.Se()&&Gd(this.Te(),b.Te())}return false};_.Ad=function Lj(){var a;a=this.Te();return(a==null?0:Db(a))^this.Se()};_.Bd=function Mj(){var a,b;b=etc(this.Te());a=this.Se();return a==1?b: +b+" x "+a};var qJ=krc(1091);IZ(309,1091,{309:1,1043:1},Oj);_.Se=function Pj(){return Nj(this)};_.Te=function Qj(){return this.b.Ve()};var vG=krc(309);IZ(1012,1,cLc,Rj);_.Hd=function Sj(a){lEc(this,a)};_.Id=function Tj(){return this.d>0||this.c.b};_.Jd=function Uj(){if(this.d==0){this.b=wuc(this.c);this.d=this.b.We().a}--this.d;this.a=true;return this.b.Ve()};_.Kd=function Vj(){var a;Yd(this.a,CLc);a=this.b.We().a;if(a<=0)throw gZ(new WBc);(this.b.We().a+=-1)==0&&xuc(this.c);this.e.b=vZ(this.e.b,1); +this.a=false};_.a=false;_.d=0;var xG=krc(1012);IZ(1015,1,{},Xj);_.Ue=function Yj(a,b){Wj(this,a,b)};var yG=krc(1015);var QX=mrc();IZ(176,1,ILc);_.yd=function Zj(a){var b;if(pF(a,50)){b=a;return Gd(this.Ve(),b.Ve())&&Gd(this.We(),b.We())}return false};_.Ad=function $j(){var a,b;a=this.Ve();b=this.We();return(a==null?0:Db(a))^(b==null?0:Db(b))};_.Xe=function _j(a){throw gZ(new ytc);};_.Bd=function ak(){return this.Ve()+"="+this.We()};var AG=krc(176);IZ(1089,1074,ALc);_.Rd=function bk(){mj(this.a)}; +_.Sd=function ck(a){return nj(this.a,a)>0};_.Td=function dk(a){return Uc(this.a,a)};_.Ud=function ek(){return Zi(this.a).Ud()};_.Wd=function fk(a){return rj(this.a,a,_Kc)>0};_.Yd=function gk(){return Zi(this.a).Yd()};var rJ=krc(1089);IZ(881,1089,ALc,hk);_.Vd=function ik(){return oj(this.a)};var CG=krc(881);IZ(1090,1074,ALc);_.Rd=function jk(){mj(this.a)};_.Sd=function kk(a){var b,c;if(pF(a,309)){c=a;if(Nj(c)<=0)return false;b=nj(this.a,c.b.Ve());return b==Nj(c)}return false};_.Wd=function lk(a){var b, +c,d,e;if(pF(a,309)){c=a;b=c.b.Ve();d=Nj(c);if(d!=0){e=this.a;return Qy(e,b,d,0)}}return false};var sJ=krc(1090);IZ(882,1090,ALc,mk);_.Vd=function nk(){return pj(this.a)};_.Yd=function ok(){return duc(this.a.a)};var DG=krc(882);IZ(377,500,xLc);_.ye=function rk(){return this.Ye()};_.Ae=function sk(){return Cyc(),Cyc(),Byc};_.ve=function uk(a){return Uf(this,a)};_.xe=function wk(a){return Wf(this,a)};_.re=function qk(){var a;return a=this.f,!a?this.f=this.te():a};_.yd=function tk(a){return ly(this,a)}; +_.Ze=function vk(a){return Uf(this,a)};_.$e=function xk(a){return Wf(this,a)};_.Be=function yk(a){return Cyc(),new JAc(a)};_.Ce=function zk(a,b){return new Vi(this,a,b)};var FG=krc(377);IZ(1096,1,{647:1});_.af=function Ak(){var a;return a=this.d,!a?this.d=new Fk(this):a};_.Rd=function Bk(){ou(this.af().a._e())};_.yd=function Ck(a){return YA(this,a)};_.Ad=function Dk(){return Eyc(this.af())};_.Bd=function Ek(){return zg(this.cf())};var HG=krc(1096);IZ(599,zLc,ALc,Fk);_.Rd=function Gk(){this.a.Rd()}; +_.Sd=function Hk(a){var b,c;if(pF(a,215)){b=a;c=Bx(this.a.cf(),b.b);return!!c&&Ql(c.He(),new Wq(b.a,b.c))}return false};_.Vd=function Ik(){return this.a._e()};_.Wd=function Jk(a){var b,c;if(pF(a,215)){b=a;c=Bx(this.a.cf(),b.b);return!!c&&Rl(c.He(),new Wq(b.a,b.c))}return false};_.Yd=function Kk(){return this.a.Yd()};_.Zd=function Lk(){return this.a.bf()};var GG=krc(599);IZ(1104,1087,{100:1});var IG=krc(1104);var Mk;IZ(785,1,{},Ok);_.Cd=function Pk(){return Bn(),new Zr};var JG=krc(785);IZ(792,1,{}, +Qk);_.Ue=function Rk(a,b){Ywc(a.b,Sd(b))};var KG=krc(792);IZ(793,1,BLc,Sk);_.Od=function Tk(a){return Bt(a.a,new wxc(a.b))};var LG=krc(793);IZ(786,1,{},Uk);_.Ue=function Vk(a,b){Xr(a,b)};var MG=krc(786);IZ(787,1,BLc,Wk);_.Od=function Xk(a){return In(a.a)};var NG=krc(787);IZ(788,1,{},Yk);_.Cd=function Zk(){return bn(),new Rs};var OG=krc(788);IZ(789,1,{},$k);_.Ue=function _k(a,b){a.Nf(b)};var PG=krc(789);IZ(790,1,BLc,al);_.Od=function bl(a){return a.Pf()};var QG=krc(790);IZ(791,1,{},cl);_.Cd=function dl(){return Nk(), +new It(this.a)};var RG=krc(791);IZ(808,1,KLc,kl);_.gf=function ol(a){return(this.a.ef()&-262&a)!=0};_.ef=function ll(){return this.a.ef()&-262};_.ff=function ml(){return this.a.ff()};_.Hd=function nl(a){this.a.Hd(new sl(a,this.b))};_.hf=function pl(a){return this.a.hf(new ql(a,this.b))};var $G=krc(808);IZ(809,1,{},ql);_.jf=function rl(a){this.a.jf(this.b.Od(a))};var SG=krc(809);IZ(810,1,{},sl);_.jf=function tl(a){this.a.jf(this.b.Od(a))};var TG=krc(810);IZ(811,1,KLc,vl);_.gf=function zl(a){return(this.a& +a)!=0};_.ef=function wl(){return this.a};_.ff=function xl(){!!this.d&&(this.b=lsc(this.b,this.d.ff()));return lsc(this.b,0)};_.Hd=function yl(a){if(this.d){this.d.Hd(a);this.d=null}this.c.Hd(new El(this.e,a));this.b=0};_.hf=function Bl(a){while(true){if(!!this.d&&this.d.hf(a)){tZ(this.b,LLc)&&(this.b=vZ(this.b,1));return true}else this.d=null;if(!this.c.hf(new Cl(this,this.e)))return false}};_.a=0;_.b=0;var WG=krc(811);IZ(812,1,{},Cl);_.jf=function Dl(a){ul(this.a,this.b,a)};var UG=krc(812);IZ(813, +1,{},El);_.jf=function Fl(a){Al(this.b,this.a,a)};var VG=krc(813);IZ(485,1,KLc,Gl);_.gf=function Kl(a){return((16464|this.b)&a)!=0};_.ef=function Hl(){return 16464|this.b};_.ff=function Il(){return this.a.ff()};_.Hd=function Jl(a){this.a.zj(new Ol(a,this.c))};_.hf=function Ll(a){return this.a.Aj(new Ml(a,this.c))};_.b=0;var ZG=krc(485);IZ(806,1,{},Ml);_.kf=function Nl(a){this.a.jf(this.b.Qf(a))};var XG=krc(806);IZ(807,1,{},Ol);_.kf=function Pl(a){this.a.jf(this.b.Qf(a))};var YG=krc(807);IZ(242,1, +{242:1,101:1});_.yd=function Sl(a){return this===a};_.nf=function Ul(){return new SBc(this)};_.mf=function Tl(){return new _y(this)};var wJ=krc(242);IZ(354,242,{354:1,242:1,3:1,101:1},Vl);_.lf=function Wl(a,b){return this.a.lf(a,b)};_.yd=function Xl(a){var b;if(a===this)return true;if(pF(a,354)){b=a;return this.a.yd(b.a)}return false};_.Ad=function Yl(){return Db(this.a)};_.Bd=function Zl(){return NZ(this.a)};var _G=krc(354);IZ(314,1,{314:1,3:1},_l);_.yd=function am(a){return pF(a,314)&&a.a==this.a}; +_.Ad=function bm(){return this.a};_.Bd=function cm(){return""+this.a};_.a=0;var aH=krc(314);IZ(1028,1104,xLc);_.re=function dm(){return this.a};_.ve=function im(a){return this.of(a)};_.we=function jm(){return this.a.Bf()};_.xe=function lm(a){return this.pf(a)};_.Rd=function em(){throw gZ(new ytc);};_.se=function fm(a){return No(this.a,a)};_.te=function gm(){throw gZ(new Kqc("should never be called"));};_.ue=function hm(){throw gZ(new Kqc(MLc));};_.pf=function km(a){throw gZ(new ytc);};_.Yd=function mm(){return this.b}; +_.b=0;var bI=krc(1028);IZ(642,1028,xLc,pm);_.re=function qm(){return this.a};_.yd=function rm(a){return ly(this,a)};_.of=function tm(a){return nm(this,a)};_.ve=function um(a){var b;return b=Po(this.a,a),!b?(Bn(),Bn(),An):b};_.De=function vm(a){var b;return b=Po(this.a,a),!b?(Bn(),Bn(),An):b};_.pf=function wm(a){return om()};_.xe=function xm(a){return om()};_.Ee=function ym(a){return om()};var QH=krc(642);IZ(1035,642,xLc,Bm);var zm;var bH=krc(1035);IZ(280,1,uLc);_.Zd=function Em(){return new uFc(this.Vd())}; +_.Bd=function Fm(){return vu(Td(this,bLc).Vd())};var fH=krc(280);IZ(735,280,uLc,Gm);_.Vd=function Hm(){return new Tu(wu($t(this.a),new Xt))};var cH=krc(735);IZ(736,280,uLc,Jm);_.Vd=function Km(){return Im(this)};var eH=krc(736);IZ(442,266,wLc,Lm);_.ce=function Mm(a){return this.a[a].Vd()};var dH=krc(442);IZ(1067,1,{});_.Bd=function Nm(){return NZ(this.qf())};var nH=krc(1067);IZ(1068,1067,lLc);_.qf=function Tm(){return this.rf()};_.Zd=function Zm(){return new tFc(this,0)};_.$d=function $m(){return new KJc(null, +this.Zd())};_.Pd=function Om(a){return this.rf(),xzc()};_.Qd=function Pm(a){return this.rf(),yzc()};_.Rd=function Qm(){this.rf(),zzc()};_.Sd=function Rm(a){return this.rf().Sd(a)};_.Td=function Sm(a){return this.rf().Td(a)};_.Ud=function Um(){return this.rf().b.Ud()};_.Vd=function Vm(){return this.rf().Vd()};_.Wd=function Wm(a){return this.rf(),Czc()};_.Xd=function Xm(a){return this.rf(),Dzc()};_.Yd=function Ym(){return this.rf().b.Yd()};_._d=function _m(){return this.rf()._d()};_.ae=function an(a){return this.rf().ae(a)}; +var gH=krc(1068);IZ(66,kLc,NLc);_.Vd=function nn(){return this.uf()};_.Pd=function gn(a){return cn()};_.Qd=function hn(a){throw gZ(new ytc);};_.sf=function jn(){return dn(this)};_.Rd=function kn(){throw gZ(new ytc);};_.Sd=function ln(a){return a!=null&&Sc(this,a,false)};_.tf=function mn(){switch(this.Yd()){case 0:return Bn(),Bn(),An;case 1:return Bn(),new Wz(Sd(this.uf().Jd()));default:return new Xs(this,this._d())}};_.Wd=function on(a){return en()};_.Xd=function pn(a){return fn()};var KH=krc(66); +IZ(458,66,NLc,qn);_.Vd=function vn(){return xu(this.a.Vd())};_.Sd=function rn(a){return a!=null&&this.a.Sd(a)};_.Td=function sn(a){return this.a.Td(a)};_.Ud=function tn(){return this.a.Ud()};_.uf=function un(){return xu(this.a.Vd())};_.Yd=function wn(){return this.a.Yd()};_._d=function xn(){return this.a._d()};_.ae=function yn(a){return this.a.ae(a)};_.Bd=function zn(){return NZ(this.a)};var hH=krc(458);IZ(113,66,OLc);_.Vd=function Mn(){return this.uf()};_.de=function Nn(){return this.vf(0)};_.ee= +function Pn(a){return this.vf(a)};_.Zd=function Sn(){return new tFc(this,16)};_.he=function Un(a,b){return this.wf(a,b)};_.be=function En(a,b){throw gZ(new ytc);};_.sf=function Fn(){return this};_.yd=function Jn(a){return Kw(this,a)};_.Ad=function Kn(){return Lw(this)};_.uf=function Ln(){return this.vf(0)};_.vf=function On(a){return Cn(this,a)};_.fe=function Rn(a){throw gZ(new ytc);};_.wf=function Tn(a,b){var c;return Vn((c=new Uw(this),new Ruc(c,a,b)))};var An;var RH=krc(113);IZ(1070,113,OLc);_.Vd= +function go(){return xu(this.xf().Vd())};_.he=function jo(a,b){return Vn(this.xf().he(a,b))};_.Sd=function $n(a){return Wn(this,a)};_.Td=function _n(a){return this.xf().Td(a)};_.yd=function ao(a){return Xn(this,a)};_.ce=function bo(a){return Yn(this,a)};_.Ad=function co(){return Db(this.xf())};_.Ud=function eo(){return this.xf().Ud()};_.uf=function fo(){return xu(this.xf().Vd())};_.Yd=function ho(){return this.xf().Yd()};_.wf=function io(a,b){return Vn(this.xf().he(a,b))};_._d=function ko(){return this.xf().ae(zE(iW, +mLc,1,this.xf().Yd(),5,1))};_.ae=function lo(a){return Zn(this,a)};_.Bd=function mo(){return NZ(this.xf())};var iH=krc(1070);IZ(134,1,QLc);_.He=function Ao(){return po(this)};_.we=function Go(){return this.Bf()};_.Le=function Mo(){return this.Cf()};_.Rd=function to(){throw gZ(new ytc);};_.se=function uo(a){return this.Ie(a)!=null};_.Ge=function vo(a){return this.Cf().Sd(a)};_.zf=function xo(){return new rs(this)};_.Af=function yo(){return new xs(this)};_.yd=function Bo(a){return xx(this,a)};_.Ad= +function Do(){return po(this).Ad()};_.Ud=function Eo(){return this.Yd()==0};_.Bf=function Fo(){return qo(this)};_.Je=function Io(a,b){return ro()};_.Ke=function Jo(a){throw gZ(new ytc);};_.Bd=function Ko(){return Dx(this)};_.Cf=function Lo(){return so(this)};_.e=null;_.f=null;_.g=null;var no;var _H=krc(134);IZ(235,134,QLc);_.se=function So(a){return No(this,a)};_.Ge=function To(a){return pAc(this.d,a)};_.yf=function Uo(){return vp(new gp(this))};_.zf=function Vo(){return vp(sAc(this.d))};_.Af=function Wo(){return bn(), +new qn(tAc(this.d))};_.yd=function Xo(a){return Oo(this,a)};_.Ie=function Yo(a){return Po(this,a)};_.Ad=function Zo(){return Db(this.d.d)};_.Ud=function $o(){return this.d.d.Ud()};_.Yd=function _o(){return this.d.d.Yd()};_.Bd=function ap(){return NZ(this.d.d)};var kH=krc(235);IZ(1069,1068,ALc);_.qf=function bp(){return this.Df()};_.rf=function cp(){return this.Df()};_.Zd=function fp(){return new tFc(this,1)};_.yd=function dp(a){return a===this||this.Df().yd(a)};_.Ad=function ep(){return this.Df().Ad()}; +var oH=krc(1069);IZ(710,1069,ALc,gp);_.qf=function ip(){return qAc(this.a.d)};_.rf=function jp(){return qAc(this.a.d)};_.Sd=function hp(a){if(pF(a,50)&&a.Ve()==null)return false;return NAc(qAc(this.a.d),a)};_.Df=function kp(){return qAc(this.a.d)};_.ae=function lp(a){var b;b=OAc(qAc(this.a.d),a);qAc(this.a.d).b.Yd()=0};_.Zd=function et(){return il(this.a.b.b.Yd(),new ft(this.b),this.a.a.a.Pe())};var fI=krc(973);IZ(974,1,{},ft);_.Qf=function gt(a){return Yn(this.a,a)};var eI=krc(974);IZ(364,235,{134:1,364:1,3:1,51:1},it);_.zf=function jt(){return At(this.a,sAc(this.c))};_.Bf=function kt(){var a;return a=this.b,!a?this.b=At(this.a,sAc(this.c)):a};_.we=function lt(){var a;return a=this.b,!a?this.b=At(this.a,sAc(this.c)):a};var hI=krc(364);IZ(839,133,{},qt); +_.Kf=function rt(){return ot(this)};_.Lf=function st(a,b){return Ywc(this.b,(oo(),el(a,b),new Wq(a,b))),this};_.Mf=function tt(a){return fs(this,a),this};var gI=krc(839);IZ(386,334,{66:1,135:1,386:1,3:1,15:1,21:1,35:1,154:1});_.Vd=function Dt(){return xu(new Wzc(this.b.b.Vd()))};_.Zd=function Et(){return new BFc(this)};_.Pe=function xt(){return this.a.a.Pe()};_.Sd=function yt(a){return a!=null&&Azc(this.a,a)};_.Td=function zt(a){var b,c;for(c=a.Vd();c.Id();){b=c.Jd();if(b==null)return false}return Bzc(this.a, +a)};_.uf=function Ct(){return xu(new Wzc(this.b.b.Vd()))};_._d=function Ft(){return fz(this,zE(iW,mLc,1,this.b.b.Yd(),5,1))};_.ae=function Gt(a){return gz(this,a)};var ut,vt;var jI=krc(386);IZ(467,112,{112:1,467:1},It);_.Jf=function Jt(a){return Ywc(this.b,Sd(a)),this};_.Nf=function Kt(a){return Ywc(this.b,Sd(a)),this};_.Of=function Lt(a){return Qs(this,a),this};_.Pf=function Mt(){return Bt(this.a,new wxc(this.b))};var iI=krc(467);IZ(827,1,{},Nt);_.Qf=function Ot(a){return qs(this.a,a)};var kI=krc(827); +IZ(486,1062,OLc,Pt);_.If=function Qt(){return this.a};_.ce=function Rt(a){return qs(this.a,a)};_.Yd=function St(){return this.a.a.Yd()};var lI=krc(486);IZ(109,1,_Lc,Xt);_.Od=function Yt(a){return a.Vd()};_.yd=function Zt(a){return this===a};var nI=krc(109);IZ(732,280,uLc,_t);_.Vd=function au(){return $t(this)};_.Zd=function bu(){return jl(this.a.Zd(),this.b)};var oI=krc(732);IZ(733,280,uLc,du);_.Vd=function eu(){return cu(this)};_.Zd=function fu(){return JJc(HJc(XA(this.a),this.b))};_.b=0;var qI= +krc(733);IZ(734,1,cLc,hu);_.Hd=function iu(a){lEc(this,a)};_.Id=function ju(){return Pb(this.b)};_.Jd=function ku(){return gu(this)};_.Kd=function lu(){Yd(!this.a,CLc);Rb()};_.a=true;var pI=krc(734);IZ(751,105,vLc,yu);_.Id=function zu(){return this.a.Id()};_.Jd=function Au(){return this.a.Jd()};var sI=krc(751);IZ(470,105,vLc,Bu);_.Id=function Cu(){return!this.a};_.Jd=function Du(){if(this.a)throw gZ(new jFc);this.a=true;return this.b};_.a=false;var rI=krc(470);IZ(279,1,cLc);_.Hd=function Fu(a){lEc(this, +a)};_.Id=function Gu(){return this.b.Id()};_.Jd=function Hu(){return this.Rf(this.b.Jd())};_.Kd=function Iu(){this.b.Kd()};var ZJ=krc(279);IZ(752,279,cLc,Ku);_.Rf=function Lu(a){return Ju(this,a)};var tI=krc(752);IZ(750,266,wLc,Ou);_.ce=function Pu(a){return this.a[this.b+a]};_.b=0;var Mu;var uI=krc(750);IZ(85,1,{85:1,32:1},Tu);_.Hd=function Uu(a){lEc(this,a)};_.Id=function Vu(){return Ru(this)};_.Jd=function Wu(){return Su(this)};_.Kd=function Xu(){Yd(!!this.c,CLc);this.c.Kd();this.c=null};var vI= +krc(85);IZ(346,12,{346:1,3:1,20:1,12:1,32:1},$u);_.Hd=function _u(a){lEc(this,a)};_.Id=function av(){return false};_.Jd=function bv(){throw gZ(new jFc);};_.Kd=function cv(){Yd(false,CLc)};var Yu;var wI=lrc(346,XV,dv);IZ(938,377,xLc);var AI=krc(938);IZ(301,938,xLc,hv);_.ye=function jv(){return wx(this.b),new ZEc};_.Rd=function iv(){fv(this)};_.Ye=function kv(){return wx(this.b),new ZEc};_.ze=function lv(a){return new vv(this,a,this.b)};_.we=function mv(){return gv(this)};_.b=2;var BI=krc(301);IZ(249, +75,{176:1,75:1,249:1,1115:1,3:1,50:1},pv);_.Sf=function qv(){return this.f};_.Tf=function rv(a){this.c=a};_.Uf=function sv(a){this.f=a};_.d=0;var xI=krc(249);IZ(939,1074,{1115:1,15:1,21:1,35:1},vv);_.Pd=function wv(a){var b,c,d,e,f;f=yZ(rZ(VLc,Vrc(yZ(rZ(a==null?0:Db(a),WLc)),15)));b=f&this.b.length-1;e=this.b[b];for(c=e;c;c=c.a)if(c.d==f&&Gd(c.i,a))return false;d=new pv(this.c,a,f,e);ov(this.d,d);d.f=this;this.d=d;nv(this.g.a.b,d);nv(d,this.g.a);this.b[b]=d;++this.f;++this.e;tv(this);return true}; +_.Rd=function xv(){var a,b;Jxc(this.b);this.f=0;for(a=this.a;a!=this;a=a.Sf()){b=a;nv(b.b,b.e)}this.a=this;this.d=this;++this.e};_.Sd=function yv(a){var b,c;c=yZ(rZ(VLc,Vrc(yZ(rZ(a==null?0:Db(a),WLc)),15)));for(b=this.b[c&this.b.length-1];b;b=b.a)if(b.d==c&&Gd(b.i,a))return true;return false};_.Sf=function zv(){return this.a};_.Vd=function Av(){return new Gv(this)};_.Wd=function Bv(a){return uv(this,a)};_.Tf=function Cv(a){this.d=a};_.Uf=function Dv(a){this.a=a};_.Yd=function Ev(){return this.f}; +_.e=0;_.f=0;var zI=krc(939);IZ(940,1,cLc,Gv);_.Hd=function Hv(a){lEc(this,a)};_.Id=function Iv(){return Fv(this),this.b!=this.c};_.Jd=function Jv(){var a,b;Fv(this);if(this.b==this.c)throw gZ(new jFc);a=this.b;b=a.i;this.d=a;this.b=a.f;return b};_.Kd=function Kv(){Fv(this);Yd(!!this.d,CLc);uv(this.c,this.d.i);this.a=this.c.e;this.d=null};_.a=0;var yI=krc(940);IZ(498,1087,xLc,Pv);_.re=function Qv(){var a;return a=this.f,!a?this.f=new ny(this):a};_.yd=function Wv(a){return ly(this,a)};_.ve=function Xv(a){return new gw(this, +a)};_.xe=function $v(a){return Nv(this,a)};_.Rd=function Sv(){this.a=null;this.e=null;cuc(this.b);this.d=0;++this.c};_.se=function Tv(a){return Mv(this,a)};_.te=function Uv(){return new ny(this)};_.ue=function Vv(){return new jw(this)};_.De=function Yv(a){return new gw(this,a)};_.Ud=function Zv(){return!this.a};_.Ee=function _v(a){return Nv(this,a)};_.Yd=function aw(){return this.d};_.c=0;_.d=0;var II=krc(498);IZ(1076,nLc,oLc);_.be=function bw(a,b){var c;c=this.ee(a);c.oe(b)};_.ce=function cw(b){var c; +c=this.ee(b);try{return c.Jd()}catch(a){a=fZ(a);if(pF(a,72))throw gZ(new Fqc("Can't get element "+b));else throw gZ(a);}};_.Vd=function dw(){return new Cw(this.a,this.b,0)};_.fe=function ew(b){var c,d;c=this.ee(b);try{d=c.Jd();c.Kd();return d}catch(a){a=fZ(a);if(pF(a,72))throw gZ(new Fqc("Can't remove element "+b));else throw gZ(a);}};var OW=krc(1076);IZ(295,1076,oLc,gw);_.ee=function hw(a){return fw(this,a)};_.Yd=function iw(){var a;a=Xtc(this.a.b,this.b);return!a?0:a.a};var DI=krc(295);IZ(499,1074, +ALc,jw);_.Sd=function kw(a){return Mv(this.a,a)};_.Vd=function lw(){return new pw(this.a)};_.Wd=function mw(a){return!Nv(this.a,a).a.Ud()};_.Yd=function nw(){return duc(this.a.b)};var CI=krc(499);IZ(872,1,cLc,pw);_.Hd=function qw(a){lEc(this,a)};_.Id=function rw(){ow(this);return!!this.c};_.Jd=function sw(){ow(this);Rv(this.c);this.a=this.c;jDc(this.d,this.a.a);do this.c=this.c.b;while(!!this.c&&!jDc(this.d,this.c.a));return this.a.a};_.Kd=function tw(){ow(this);Yd(!!this.a,CLc);ou(new Bw(this.e, +this.a.a));this.a=null;this.b=this.e.c};_.b=0;var EI=krc(872);IZ(376,1,{376:1},uw);_.a=0;var FI=krc(376);IZ(871,176,ILc,vw);_.Ve=function ww(){return this.a};_.We=function xw(){return this.f};_.Xe=function yw(a){var b;b=this.f;this.f=a;return b};var GI=krc(871);IZ(244,1,ELc,Bw,Cw);_.Hd=function Ew(a){lEc(this,a)};_.oe=function Dw(a){this.e=Lv(this.f,this.b,a,this.c);++this.d;this.a=null};_.Id=function Fw(){return!!this.c};_.pe=function Gw(){return!!this.e};_.Jd=function Hw(){return zw(this)};_.qe= +function Iw(){return Aw(this)};_.Kd=function Jw(){Yd(!!this.a,CLc);if(this.a!=this.c){this.e=this.a.e;--this.d}else this.c=this.a.c;Ov(this.f,this.a);this.a=null};_.d=0;var HI=krc(244);IZ(738,nLc,oLc);_.be=function Pw(a,b){this.a.be(a,b)};_.Sd=function Qw(a){return this.a.Sd(a)};_.ce=function Rw(a){return this.a.ce(a)};_.fe=function Sw(a){return this.a.fe(a)};_.Yd=function Tw(){return this.a.Yd()};var KI=krc(738);IZ(739,738,DLc);var LI=krc(739);IZ(741,739,DLc,Uw);_.ee=function Vw(a){return this.a.ee(a)}; +var JI=krc(741);IZ(173,nLc,{173:1,15:1,21:1,54:1},Zw);_.be=function $w(a,b){this.a.be(Yw(this,a),b)};_.Rd=function _w(){this.a.Rd()};_.ce=function ax(a){return this.a.ce(Xw(this,a))};_.Vd=function bx(){return Ww(this,0)};_.ee=function cx(a){return Ww(this,a)};_.fe=function dx(a){return this.a.fe(Xw(this,a))};_.ge=function ex(a,b){(Wd(a,b,this.a.Yd()),Ow(this.a.he(Yw(this,b),Yw(this,a)))).Rd()};_.Yd=function fx(){return this.a.Yd()};_.he=function gx(a,b){return Wd(a,b,this.a.Yd()),Ow(this.a.he(Yw(this, +b),Yw(this,a)))};var OI=krc(173);IZ(468,173,{173:1,15:1,21:1,54:1,69:1},hx);var MI=krc(468);IZ(740,1,ELc,ix);_.Hd=function kx(a){lEc(this,a)};_.oe=function jx(a){this.b.oe(a);this.b.qe();this.a=false};_.Id=function lx(){return this.b.pe()};_.pe=function mx(){return this.b.Id()};_.Jd=function nx(){if(!this.b.pe())throw gZ(new jFc);this.a=true;return this.b.qe()};_.qe=function ox(){if(!this.b.Id())throw gZ(new jFc);this.a=true;return this.b.Jd()};_.Kd=function px(){Yd(this.a,CLc);this.b.Kd();this.a= +false};_.a=false;var NI=krc(740);IZ(342,113,OLc,qx);_.ce=function rx(a){return Rd(a,this.a.length),_qc(zsc(this.a,a))};_.he=function ux(a,b){return Wd(a,b,this.a.length),new qx(Sd(Vsc(this.a,a,b)))};_.Yd=function sx(){return this.a.length};_.wf=function tx(a,b){return Wd(a,b,this.a.length),new qx(Sd(Vsc(this.a,a,b)))};var QI=krc(342);IZ(727,279,cLc,Fx);_.Rf=function Gx(a){return a.Ve()};var RI=krc(727);IZ(728,279,cLc,Hx);_.Rf=function Ix(a){return a.We()};var SI=krc(728);IZ(729,279,cLc,Jx);_.Rf=function Kx(a){return new Wq(a, +this.a.Od(a))};var TI=krc(729);IZ(726,1075,ALc,Lx);_.Vd=function Mx(){return this.a.Ff()};_.Oe=function Nx(){return this.a};_.Zd=function Ox(){return this.a.Gf()};var VI=krc(726);IZ(725,kLc,lLc,Px);_.Rd=function Qx(){this.a.Rd()};_.Sd=function Rx(a){return this.a.Ge(a)};_.Ud=function Sx(){return this.a.Ud()};_.Vd=function Tx(){return new Hx(this.a.He().Vd())};_.Wd=function Ux(b){var c,d;try{return Sc(this,b,true)}catch(a){a=fZ(a);if(pF(a,28)){for(d=this.a.He().Vd();d.Id();){c=d.Jd();if(Gd(b,c.We())){this.a.Ke(c.Ve()); +return true}}return false}else throw gZ(a);}};_.Xd=function Vx(b){var c,d,e;try{return Vc(this,Sd(b))}catch(a){a=fZ(a);if(pF(a,28)){e=new mDc;for(d=this.a.He().Vd();d.Id();){c=d.Jd();b.Sd(c.We())&&jDc(e,c.Ve())}return this.a.we().Xd(e)}else throw gZ(a);}};_.Yd=function Wx(){return this.a.Yd()};var YI=krc(725);IZ(1098,1,{});var iJ=krc(1098);IZ(1101,1,{});var gJ=krc(1101);IZ(412,1101,{},$x);_.a=0;var $I=krc(412);IZ(963,1,ULc,_x);_.Cd=function ay(){return new gxc};var _I=krc(963);IZ(964,1,ULc,by);_.Cd= +function cy(){return wx(this.a),new nDc};_.a=0;var aJ=krc(964);IZ(965,1,ULc,dy);_.Cd=function ey(){return wx(this.a),new ZEc};_.a=0;var bJ=krc(965);IZ(1099,1098,{});var cJ=krc(1099);IZ(966,1099,{},gy);_.b=0;var dJ=krc(966);IZ(1100,1098,{});var hJ=krc(1100);IZ(967,1100,{},iy);_.b=0;var eJ=krc(967);IZ(968,1100,{},ky);_.b=0;var fJ=krc(968);IZ(502,1088,yLc,ny);_.Ie=function ry(a){return this.a.se(a)?this.a.ve(a):null};_.Ke=function uy(a){return this.a.se(a)?this.a.xe(a):null};_.Rd=function oy(){this.a.Rd()}; +_.se=function py(a){return this.a.se(a)};_.Ne=function qy(){return new wy(this)};_.Me=function(){return this.Ne()};_.Ud=function sy(){return this.a.Ud()};_.we=function ty(){return this.a.we()};_.Yd=function vy(){return this.a.we().Yd()};var lJ=krc(502);IZ(875,1075,ALc,wy);_.Vd=function xy(){return vx(this.a.a.we(),new Ay(this))};_.Oe=function yy(){return this.a};_.Wd=function zy(a){var b;if(!nh(this,a))return false;b=a;my(this.a,b.Ve());return true};var kJ=krc(875);IZ(876,1,_Lc,Ay);_.Od=function By(a){return this.a.a.a.ve(a)}; +_.yd=function Cy(a){return this===a};var jJ=krc(876);IZ(874,873,xLc,Dy);_.Vf=function Fy(){return this.a.Cd()};_.ye=function(){return this.Vf()};_.te=function Ey(){return new Wg(this,this.c)};_.ue=function Gy(){return new Oh(this,this.c)};var mJ=krc(874);IZ(501,377,xLc,Hy);_.Vf=function Jy(){return this.a.Cd()};_.ye=function(){return this.Vf()};_.te=function Iy(){return new Wg(this,this.c)};_.Wf=function Ky(){return this.a.Cd()};_.Ye=function(){return this.Wf()};_.ue=function Ly(){return new Oh(this, +this.c)};_.Be=function My(a){return pF(a,316)?wz(a):pF(a,154)?(Cyc(),new kBc(a)):(Cyc(),new JAc(a))};_.Ce=function Ny(a,b){return pF(b,316)?new Ti(this,a,b):pF(b,154)?new Pi(this,a,b):new Vi(this,a,b)};var nJ=krc(501);IZ(883,1,{},Sy);var pJ=krc(883);IZ(509,1,BLc,Ty);_.Od=function Uy(a){return new tFc(Gyc(Nj(a),a.b.Ve()),16)};var tJ=krc(509);IZ(932,242,{242:1,3:1,101:1},Xy);_.lf=function Yy(a,b){return Sd(a),Sd(b),Oqc(a,b)};_.mf=function Zy(){var a;a=this.a;!a&&(a=this.a=new _y(this));return a};_.Bd= +function $y(){return"Ordering.natural()"};var Vy;var uJ=krc(932);IZ(315,242,{315:1,242:1,3:1,101:1},_y);_.lf=function az(a,b){if(uF(a)===uF(b))return 0;if(a==null)return-1;if(b==null)return 1;return this.a.lf(a,b)};_.yd=function bz(a){var b;if(a===this)return true;if(pF(a,315)){b=a;return this.a.yd(b.a)}return false};_.Ad=function cz(){return Db(this.a)^957692532};_.mf=function dz(){return this};_.Bd=function ez(){return this.a+".nullsFirst()"};var vJ=krc(315);IZ(114,1070,OLc,hz);_.xf=function iz(){return this.a}; +var yJ=krc(114);IZ(272,235,QLc,jz,kz);var zJ=krc(272);IZ(335,334,RLc,nz);var lz;var AJ=krc(335);IZ(248,386,{66:1,135:1,386:1,248:1,3:1,15:1,21:1,35:1,154:1},oz);_.tf=function pz(){return new at(this,(Bn(),Vn(new eyc(fz(this,zE(iW,mLc,1,this.b.b.Yd(),5,1))))))};var BJ=krc(248);IZ(1073,zLc,ALc);_.Vd=function Az(){return new Kz(this.a,this.b)};_.Pd=function xz(a){throw gZ(new ytc);};_.Qd=function yz(a){throw gZ(new ytc);};_.Rd=function zz(){throw gZ(new ytc);};_.Wd=function Bz(a){throw gZ(new ytc);}; +_.Xd=function Cz(a){throw gZ(new ytc);};var GJ=krc(1073);IZ(720,1073,ALc,Dz);_.Vd=function Gz(){return new Kz(this.a,this.b)};_.Sd=function Ez(a){return this.a.Sd(a)||this.b.Sd(a)};_.Ud=function Fz(){return this.a.Ud()&&this.b.Ud()};_.Yd=function Hz(){var a,b,c;c=this.a.Yd();for(b=this.b.Vd();b.Id();){a=b.Jd();this.a.Sd(a)||++c}return c};_.$d=function Iz(){return uJc(this.a.$d(),EJc(new KJc(null,this.b.Zd()),new Lz(this.a)))};var EJ=krc(720);IZ(459,721,vLc,Kz);var CJ=krc(459);IZ(722,1,{49:1},Lz); +_.Nd=function Mz(a){return!this.a.Sd(a)};var DJ=krc(722);IZ(336,1072,{336:1,3:1,15:1,21:1,316:1,35:1,154:1},Nz);_.qf=function Oz(){return this.b};_.rf=function Pz(){return this.b};_.Df=function Qz(){return this.b};_.$d=function Rz(){return this.a.$d()};var HJ=krc(336);IZ(360,828,QLc,Sz);_.Cf=function Tz(){return bn(),new Zz(this.a)};_.Le=function Uz(){return bn(),new Zz(this.a)};_.df=function Vz(){return bn(),new Zz(this.a)};var IJ=krc(360);IZ(70,1070,OLc,Wz);_.xf=function Xz(){return this.a};var JJ= +krc(70);IZ(88,135,RLc,Zz);_.Vd=function aA(){return new Bu(this.a)};_.Sd=function $z(a){return Yz(this,a)};_.uf=function _z(){return new Bu(this.a)};_.Yd=function bA(){return 1};var KJ=krc(88);IZ(955,1,cLc,dA);_.Hd=function eA(a){lEc(this,a)};_.Jd=function gA(){return cA(this)};_.Id=function fA(){return QEc(this.c)||this.a.Id()};_.Kd=function hA(){this.a.Kd();if(this.b.e.Ud()){SEc(this.c);this.b=null}};var LJ=krc(955);IZ(597,1093,yLc,jA);_.Rd=function kA(){var a;a=!this.a||this.a.Ud()&&oEc(this.c.a, +this.b)?this.a=pEc(this.c.a,this.b):this.a;!!a&&a.Rd();iA(this)};_.se=function lA(a){var b;b=!this.a||this.a.Ud()&&oEc(this.c.a,this.b)?this.a=pEc(this.c.a,this.b):this.a;return a!=null&&!!b&&Ax(b,a)};_.Ff=function mA(){var a,b;b=!this.a||this.a.Ud()&&oEc(this.c.a,this.b)?this.a=pEc(this.c.a,this.b):this.a;if(!b)return Zu(),Yu;a=b.He().Vd();return new uA(this,a)};_.Gf=function nA(){var a;a=!this.a||this.a.Ud()&&oEc(this.c.a,this.b)?this.a=pEc(this.c.a,this.b):this.a;if(!a)return TFc(),SFc;return jl(a.He().Zd(), +new sA)};_.Ie=function oA(a){var b;b=!this.a||this.a.Ud()&&oEc(this.c.a,this.b)?this.a=pEc(this.c.a,this.b):this.a;return a!=null&&!!b?Bx(b,a):null};_.Je=function pA(a,b){Sd(a);Sd(b);if(!!this.a&&!this.a.Ud())return this.a.Je(a,b);return Vp(this.c,this.b,a,b)};_.Ke=function qA(a){var b,c;b=!this.a||this.a.Ud()&&oEc(this.c.a,this.b)?this.a=pEc(this.c.a,this.b):this.a;if(!b)return null;c=Cx(b,a);iA(this);return c};_.Yd=function rA(){var a;a=!this.a||this.a.Ud()&&oEc(this.c.a,this.b)?this.a=pEc(this.c.a, +this.b):this.a;return!a?0:a.Yd()};var SJ=krc(597);IZ(959,1,BLc,sA);_.Od=function tA(a){return new zA(a)};var MJ=krc(959);IZ(958,1,cLc,uA);_.Hd=function vA(a){lEc(this,a)};_.Jd=function xA(){return new zA(this.b.Jd())};_.Id=function wA(){return this.b.Id()};_.Kd=function yA(){this.b.Kd();iA(this.a)};var NJ=krc(958);IZ(598,1097,SLc,zA);_.qf=function AA(){return this.a};_.yd=function BA(a){return Ip(this,a)};_.Xe=function CA(a){return Hp(this,Sd(a))};var OJ=krc(598);IZ(411,1088,yLc,DA);_.Ie=function GA(a){return this.a.Ef(a)? +new jA(this.a,a):null};_.Ke=function HA(a){return a==null?null:sEc(this.a.a,a)};_.se=function EA(a){return this.a.Ef(a)};_.Ne=function FA(){return new LA(this)};_.Me=function(){return this.Ne()};var RJ=krc(411);IZ(956,1074,ALc);_.Rd=function JA(){nEc(this.b.a)};_.Ud=function KA(){return duc(this.b.a.c)==0};var TJ=krc(956);IZ(957,956,ALc,LA);_.Sd=function MA(a){var b;if(pF(a,50)){b=a;return b.Ve()!=null&&pF(b.We(),51)&&Ql(new KEc(this.a.a.a),b)}return false};_.Vd=function NA(){return vx(new Wuc(this.a.a.a), +new QA(this))};_.Wd=function OA(a){var b;if(pF(a,50)){b=a;return b.Ve()!=null&&pF(b.We(),51)&&JEc(new KEc(this.a.a.a),b)}return false};_.Yd=function PA(){return duc(this.a.a.a.c)};var QJ=krc(957);IZ(960,1,_Lc,QA);_.Od=function RA(a){return new jA(this.a.a.a,a)};_.yd=function SA(a){return this===a};var PJ=krc(960);IZ(962,1,BLc,TA);_.Od=function UA(a){return jl(a.We().He().Zd(),new VA(a))};var UJ=krc(962);IZ(961,1,BLc,VA);_.Od=function WA(a){return aq(this.a,a)};var VJ=krc(961);IZ(1105,1,{1116:1}); +_.yd=function ZA(a){var b;if(a===this)return true;if(pF(a,215)){b=a;return Gd(this.b,b.b)&&Gd(this.a,b.a)&&Gd(this.c,b.c)}return false};_.Ad=function $A(){return Pxc(CE(xE(iW,1),mLc,1,5,[this.b,this.a,this.c]))};_.Bd=function _A(){return"("+this.b+","+this.a+")="+this.c};var XJ=krc(1105);IZ(215,1105,{1116:1,215:1,3:1},aB);var YJ=krc(215);IZ(1092,1,{});var bB;var fK=krc(1092);IZ(247,1,{247:1},kB);_.yd=function lB(a){return hB(this,a)};_.Ad=function mB(){return Oxc(this.c)};_.Bd=function nB(){return this.f}; +_.a=0;_.b=0;_.d=0;var aK=krc(247);IZ(150,1092,{150:1},rB);_.yd=function sB(a){var b;if(pF(a,150)){b=a;return hB(this.b,b.b)&&Gd(this.c,b.c)}return false};_.Ad=function tB(){return Oxc(this.b.c)^Pxc(CE(xE(iW,1),mLc,1,5,[this.c]))};_.Bd=function uB(){var a;a=new vtc("BaseEncoding.");ptc(a,this.b.f);8%this.b.a!=0&&(!this.c?(a.a+=".omitPadding()",a):ptc(otc((a.a+=".withPadChar('",a),this.c),"')"));return a.a};var eK=krc(150);IZ(513,150,{150:1},wB);var bK=krc(513);IZ(380,150,{150:1},zB);var cK=krc(380); +IZ(36,1,{3:1,36:1});_.Xf=function JB(a){return new Error(a)};_.Yf=function LB(){return this.g};_.Zf=function MB(){var a,b,c;c=this.g==null?null:this.g.replace(new RegExp(gMc,"g"),"\u200b\n");b=(a=frc(this.Fj),c==null?a:a+": "+c);DB(this,KB(this.Xf(b)));jE(this)};_.Bd=function NB(){return FB(this,this.Yf())};_.e=eMc;_.k=true;var oW=krc(36);IZ(38,36,{3:1,38:1,36:1});var ZV=krc(38);IZ(104,38,{104:1,3:1,38:1,36:1});var OV=krc(104);IZ(204,104,{204:1,104:1,3:1,38:1,36:1},QB);var dK=krc(204);var WB;IZ(118, +1,{},_B);_.Bd=function aC(){return Cd(Ad(Ad(new Dd((drc(gK),gK.n)),"line",""+this.b),"column",""+this.a))};_.a=0;_.b=0;var gK=krc(118);IZ(477,1,{},iC);_.b=0;_.c=null;var nK=krc(477);IZ(780,1,{},kC);var hK=krc(780);IZ(781,1,{},qC);_.b=0;_.c=0;_.d=0;_.e=0;_.f=0;_.g=0;var iK=krc(781);IZ(478,1,jMc,rC);_.$f=function sC(){return this.e};_._f=function tC(){return-1};_.ag=function uC(){return-1};_.bg=function vC(){return-1};_.cg=function wC(){return-1};_.e=0;var lK=krc(478);IZ(479,478,jMc,xC);_._f=function yC(){return-1}; +_.ag=function zC(){return this.b};_.bg=function AC(){return this.c};_.cg=function BC(){return this.d};_.b=0;_.c=0;_.d=0;var mK=krc(479);IZ(783,479,jMc,CC);_._f=function DC(){return this.a};_.a=0;var jK=krc(783);IZ(782,1,{},HC);_.b=0;_.c=0;var kK=krc(782);IZ(802,1,{},PC);_.c=null;_.d=-1;var tK=krc(802);IZ(803,1,{},TC);var oK=krc(803);IZ(805,1,{},WC);_.dg=function XC(a,b,c,d,e){var f;this.d!=b&&(this.c=0);if(b!=d||c!=e)if(b=0);new _B(c,b)}};_.Xg=function q5(){return this.j.a.length>0?uqc(this.j,this.j.a.length-1):0};_.eh=function r5(a){var b,c,d,e;Xd(!!this.q);Xd(!!a);if(this.k&&(b=H8b(a,(Hbc(),ybc)),(!b? +null:b.ug())!=null)&&T9b(a.k)>0&&true){d=this.n;c=this.o;Xd(d>=0);e=new B5;e.b=a;e.c=new _B(d,c);null.Ij();null.Ij()}};_.k=false;_.n=0;_.o=0;_.p=0;var kL=krc(511);IZ(885,511,{},u5);_.Sg=function v5(){var a;if(!this.c)return;if(this.f);else if(this.o>(this.p/2|0));else if(this.e>0){wqc(this.j,this.e);this.b=this.g;this.o=this.j.a.length-this.b;a=this.e-this.g+1;l5(this,this.n,a,false);--this.n;this.e=0;this.g=0}else return;V3(this);t5(this)};_.Zg=function w5(){s5(this)};_._g=function x5(){var a,b; +if(this.a)if(this.r){t5(this);this.r=false}b=this.j.a.length;if(this.d==b-1){a=uqc(this.j,b-1);a==59&&(this.d=b)}s5(this)};_.ah=function y5(){this.d=this.j.a.length};_.dh=function z5(){t5(this)};_.a=false;_.b=0;_.c=false;_.d=0;_.e=0;_.f=false;_.g=0;var iL=krc(885);IZ(510,1,{510:1},B5);_.Bd=function C5(){return"Mapping: start "+this.c+", end "+this.a+", node "+this.b};var jL=krc(510);IZ(884,511,{},F5);_.Kg=function G5(b,c){var d,e;if(b==0&&1/b<0){M3(this,b);return}e=S5(c);if(e==null){M3(this,b);return}b< +0&&(e="-"+e);try{d=Brc(e)}catch(a){a=fZ(a);if(pF(a,82)){M3(this,b);return}else throw gZ(a);}if(b!=d){M3(this,b);return}L3(this,e)};_.Lg=function H5(a){D5(this,a)};_.Mg=function I5(){V3(this);E5(this);--this.a;L3(this,"}")};_.Ng=function J5(){(this.j.a.length>0?uqc(this.j,this.j.a.length-1):0)!=32&&(this.j.a.length>0?uqc(this.j,this.j.a.length-1):0)!=10&&L3(this," ");L3(this,"{");++this.a};_.Og=function K5(a,b){(this.j.a.length>0?uqc(this.j,this.j.a.length-1):0)!=32&&b&&a.charCodeAt(0)!=44&&L3(this, +" ");L3(this,a);b&&L3(this," ")};_.Pg=function L5(){D5(this,":");++this.a;E5(this)};_.Qg=function M5(a,b){var c,d;Yd(a.n==(Afc(),Vcc),a);c=a.g;d=c.n;switch(d.f){case 65:case 71:return false;case 44:return a!=c.c;case 78:return!_pb(!c.g?null:c.g.g);case 66:return a==(c.c?c.c.i:null)}return true};_.Rg=function N5(){--this.a};_.Sg=function O5(){V3(this)};_.Tg=function P5(a){this.r=true;a&&E5(this);a&&E5(this)};_.Ug=function Q5(){E5(this)};_.Wg=function R5(a){L3(this,";");E5(this);this.s=false};_.Yg= +function T5(){L3(this,iLc);this.o>this.p&&E5(this)};_.Zg=function U5(){this.o>this.p&&E5(this)};_.$g=function V5(){(this.j.a.length>0?uqc(this.j,this.j.a.length-1):0)!=32&&(this.j.a.length>0?uqc(this.j,this.j.a.length-1):0)!=10&&L3(this," ")};_._g=function W5(){this.o>this.p&&E5(this)};_.bh=function X5(){return true};_.dh=function Y5(){E5(this)};_.a=0;var lL=krc(884);IZ(461,1,{},Z5);var mL=krc(461);IZ(275,12,{275:1,3:1,20:1,12:1},b6);var $5,_5;var nL=lrc(275,XV,c6);IZ(757,1,wNc,e6);_.Dg=function f6(a){return d6(a)}; +_.Eg=function g6(a){return null};_.Fg=function h6(a){return false};_.Gg=function i6(a){return false};_.Hg=function j6(a){return Esc(a.substr(0,6),"$super")};_.Ig=function k6(a){return _8b(a.c,xNc)};var oL=krc(757);var l6;IZ(437,658,{});_.ig=function I7(a,b){PHc(this.t,a,b)};_.jg=function J7(){return this.F};_.gh=function K7(){return this.G};_.kg=function(){return this.gh()};_.lg=function L7(a){var b,c,d;if(!this.H||!this.n)if(!this.H){c=N6(this,this.G.Sb);d=k9(this.G)?(KJb(),JJb):(KJb(),IJb);this.H= +(b=yOb(c,this.G.nc,(FJb(),EJb),this.G.mc,d),b);this.n=c==(zJb(),pJb)?A6(this,qJb,d):this.H}switch(a){case 1:return this.n;default:return this.H}};_.mg=function M7(){return U6(this)};_.ng=function N7(){return this.r};_.og=function O7(a){var b;b=new ttb(this);rtb(b,null,a)};_.xg=function P7(a,b){u7(this,b)};_.pg=function Q7(a){u7(this,a)};_.qg=function R7(a){v7(this,a)};_.hh=function S7(a){Xd(a.n==(Afc(),Jdc));c9b(a,(Hbc(),Pac),1);_xb(this.c,a);$xb(this.f,a)};_.rg=function(a){this.hh(a)};_.b=1;_.j= +null;_.n=null;_.r=true;_.A=null;_.G=null;_.H=null;_.I=null;_.J=null;_.K=0;_.N=null;_.Q=0;var n6,o6,p6,q6,r6,s6,t6,u6;var SL=krc(437);IZ(47,1,nOc);_.Bd=function V7(){return this.d};_.c=false;var vO=krc(47);IZ(657,47,nOc,X7);_.ih=function Y7(a){return W7(a)};_.jh=function Z7(){return iQb(),dQb};var qL=krc(657);IZ(448,1,{},_7);_.Bd=function a8(){return this.c.a};_.a=0;_.b=0;var rL=krc(448);IZ(665,1,{},b8);_.kh=function c8(){return n7(this.a),null};var sL=krc(665);IZ(666,1,{},d8);_.kh=function e8(){return b7(this.a)}; +var tL=krc(666);IZ(664,1,{},f8);_.kh=function g8(){return p7(this.a),null};var uL=krc(664);IZ(663,1,{},h8);_.kh=function i8(){return k7(this.a),null};var vL=krc(663);IZ(111,1,{220:1},j8);_.Cd=function k8(){return""+this.a.Q++};var wL=krc(111);IZ(435,1,{},l8);_.kh=function m8(){return c7(this.a)};var xL=krc(435);IZ(669,1,{},n8);_.kh=function o8(){return d7(this.a,this.b)};var yL=krc(669);IZ(444,1,{},p8);_.kh=function q8(){return e7(this.a,this.b,this.d,this.c)};_.c=0;var zL=krc(444);IZ(239,1,{431:1, +239:1,3:1});_.nh=function s8(){return xDb(this.oh())};var HQ=krc(239);IZ(144,239,{144:1,431:1,239:1,3:1},G8,H8,I8);_.qh=function K8(a){return x8(this,a)};_.rh=function L8(){return this.i};_.lh=function M8(){return y8(this).c};_.ug=function N8(){return this.i.a};_.mh=function O8(){return y8(this).f};_.oh=function P8(){return A8(this)};_.sh=function Q8(){return this.a.sh()};_.ph=function R8(){return y8(this).i};_.Bd=function S8(){return this.i.a};_.g=false;var CL=krc(144);IZ(731,1,{},W8);var AL=krc(731); +IZ(160,12,{160:1,3:1,20:1,12:1},c9);var X8,Y8,Z8,$8,_8,a9;var BL=lrc(160,XV,d9);IZ(165,1,wNc,v9);_.Bd=function x9(){var a;a=Cd(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Bd(new Dd((drc(RL), +RL.n))),"aggressiveFusion",""+this.a),"aliasableStrings",this.e),"aliasAllStrings",""+this.b),"aliasHandler",this.c),"aliasStringsBlacklist",this.d),"allowHotswapReplaceScript",SNc),"ambiguateProperties",""+this.f),"angularPass",""+this.g),"anonymousFunctionNaming",this.i),"assumeClosuresOnlyCaptureReferences",""+this.j),"assumeStrictThis",""+this.k),"browserResolverPrefixReplacements",this.o),"brokenClosureRequiresLevel",this.n),"checkDeterminism",""+this.p),"checkGlobalNamesLevel",this.q),"checkGlobalThisLevel", +this.r),"checkMissingGetCssNameBlacklist",this.s),"checkMissingGetCssNameLevel",this.t),"checksOnly",""+this.A),"checkSuspiciousCode",""+this.u),"checkSymbols",""+this.v),sOc,""+this.w),"closurePass",""+this.C),"coalesceVariableNames",""+this.D),"codingConvention",this.F),"collapseAnonymousFunctions",""+this.G),tOc,""+this.H),"collapseProperties",this.I),"collapseVariableDeclarations",""+this.J),"colorizeErrorOutput",""+this.K),"computeFunctionSideEffects",""+this.L),"conformanceConfigs",this.M), +"conformanceRemoveRegexFromPath",this.N),"continueAfterErrors",SNc),"convertToDottedProperties",""+this.O),"crossChunkCodeMotion",""+this.P),"crossChunkCodeMotionNoStubMethods",""+this.Q),"crossChunkMethodMotion",""+this.R),"cssRenamingMap",this.S),"cssRenamingWhitelist",this.T),"customPasses",this.U),"dartPass",""+this.V),"deadAssignmentElimination",""+this.W),"declaredGlobalExternsOnWindow",""+this.X),"defineReplacements",w9(this.Y)),"dependencyOptions",this.Z),"devirtualizePrototypeMethods",""+ +this._),"devMode",this.$),"disambiguatePrivateProperties",""+this.ab),"disambiguateProperties",""+this.bb),"enforceAccessControlCodingConventions",""+this.db),"environment",this.eb),"errorFormat",this.fb),"errorHandler",this.gb),"es6ModuleTranspilation",this.hb),"exportLocalPropertyDefinitions",""+this.ib),"exportTestFunctions",""+this.jb),"externExports",""+this.kb),"externExportsPath",this.lb),"extraAnnotationNames",this.mb),"extractPrototypeMemberDeclarations",this.ob),"extraSmartNameRemoval", +""+this.nb),"filesToPrintAfterEachPassRegexList",this.pb),"flowSensitiveInlineVariables",""+this.qb),"foldConstants",""+this.rb),"forceLibraryInjection",this.sb),"gatherCssNames",""+this.tb),"generateExportsAfterTypeChecking",""+this.vb),"generateExports",""+this.ub),"generatePseudoNames",""+this.wb),"generateTypedExterns",""+(this.zb==($9(),X9))),"idGenerators",this.xb),"idGeneratorsMapSerialized",this.yb),"incrementalCheckMode",this.zb),"inferConsts",""+this.Ab),"inferTypes",""+this.Bb),"inlineConstantVars", +""+this.Cb),"inlineFunctionsLevel",this.Db),"inlineGetters",""+this.Eb),"inlineLocalVariables",""+this.Fb),"inlineProperties",""+this.Gb),uOc,""+this.Hb),"inputAnonymousFunctionNamingMap",this.Ib),"inputDelimiter",this.Jb),"inputPropertyMap",this.Kb),"inputSourceMaps",this.Lb),"inputVariableMap",this.Mb),"instrumentForCoverage",""+this.Ob),"instrumentForCoverageOnly",SNc),"instrumentBranchCoverage",""+this.Nb),"j2clPassMode",this.Qb),"labelRenaming",""+this.Rb),"languageIn",this.Sb),"languageOutIsDefaultStrict", +this.Tb),"legacyCodeCompile",SNc),"lineBreak",""+this.Ub),"lineLengthThreshold",""+this.Vb),"locale",this.Wb),"markAsCompiled",""+this.Xb),"markNoSideEffectCalls",""+this.Yb),"maxFunctionSizeAfterInlining",""+this.Zb),"messageBundle",null),"moduleRoots",this._b),"chunksToPrintAfterEachPassRegexList",this.B),"moveFunctionDeclarations",""+this.ac),"nameGenerator",this.bc),"optimizeArgumentsArray",""+this.ec),"optimizeCalls",""+this.fc),"outputCharset",this.gc),"outputFeatureSet",this.hc),"outputJs", +this.ic),"outputJsStringUsage",""+this.jc),"parentChunkCanSeeSymbolsDeclaredInChildren",""+this.lc),"parseJsDocDocumentation",this.nc),"pathEscaper",this.oc),"polymerVersion",this.qc),"polymerExportPolicy",this.pc),"preferLineBreakAtEndOfFile",""+this.rc),"preferSingleQuotes",""+this.sc),"preferStableNames",""+this.tc),"preserveDetailedSourceInfo",SNc),"preserveGoogProvidesAndRequires",""+this.uc),"preserveTypeAnnotations",""+this.vc),"prettyPrint",""+this.wc),"preventLibraryInjection",SNc),"printConfig", +SNc),"printInputDelimiter",""+this.yc),"printSourceAfterEachPass",""+this.zc),"processCommonJSModules",SNc),"propertyInvalidationErrors",this.Ac),"propertyRenaming",this.Bc),"protectHiddenSideEffects",""+this.Cc),"quoteKeywordProperties",""+this.Dc),"removeAbstractMethods",""+this.Ec),"removeClosureAsserts",""+this.Fc),"removeJ2clAsserts",""+this.Hc),"removeDeadCode",""+this.Gc),vOc,""+this.Ic),"removeUnusedConstructorProperties",""+this.Jc),"removeUnusedLocalVars",""+this.Kc),"removeUnusedPrototypePropertiesInExterns", +""+this.Mc),wOc,""+this.Lc),"removeUnusedVars",""+this.Nc),"renamePrefixNamespaceAssumeCrossChunkNames",SNc),"renamePrefixNamespace",this.Pc),"renamePrefix",this.Oc),"replaceIdGenerators",""+this.Qc),"replaceMessagesWithChromeI18n",""+this.Rc),"replaceStringsFunctionDescriptions",this.Sc),"replaceStringsInputMap",this.Tc),"replaceStringsPlaceholderToken",this.Uc),"replaceStringsReservedStrings",this.Vc),"reserveRawExports",""+this.Wc),"rewriteFunctionExpressions",""+this.Yc),"rewritePolyfills",SNc), +"runtimeTypeCheckLogFunction",this.$c),"runtimeTypeCheck",""+this.Zc),"shadowVariables",""+this._c),"skipNonTranspilationPasses",""+this.ad),"smartNameRemoval",""+this.bd),"sourceMapDetailLevel",this.cd),"sourceMapFormat",this.dd),"sourceMapLocationMappings",this.ed),"sourceMapOutputPath",this.fd),"stripNamePrefixes",this.gd),"stripNameSuffixes",this.hd),"stripTypePrefixes",this.jd),"stripTypes",this.kd),"summaryDetailLevel",""+this.ld),"syntheticBlockEndMarker",this.md),"syntheticBlockStartMarker", +this.nd),"tcProjectId",this.od),"tracer",this.pd),"transformAMDToCJSModules",SNc),"trustedStrings",""+this.qd),"tweakProcessing",this.rd),"tweakReplacements",w9(this.sd)),"emitUseStrict",this.cb),"useTypesForLocalOptimization",""+this.ud),"variableRenaming",this.vd),"warningsGuard",this.wd),"wrapGoogModulesForWhitespaceOnly",""+this.xd));return a};_.a=false;_.b=false;_.f=false;_.g=false;_.j=false;_.k=false;_.p=false;_.u=false;_.v=false;_.w=false;_.A=false;_.C=false;_.D=false;_.G=false;_.H=false;_.J= +false;_.K=false;_.L=false;_.O=false;_.P=false;_.Q=false;_.R=false;_.V=false;_.W=false;_.X=false;_._=false;_.ab=false;_.bb=false;_.db=false;_.ib=false;_.jb=false;_.kb=false;_.nb=false;_.qb=false;_.rb=false;_.tb=false;_.ub=false;_.vb=false;_.wb=false;_.Ab=true;_.Bb=false;_.Cb=false;_.Eb=false;_.Fb=false;_.Gb=false;_.Hb=false;_.Jb="// Input %num%";_.Nb=false;_.Ob=false;_.Rb=false;_.Ub=false;_.Vb=500;_.Xb=false;_.Yb=false;_.Zb=0;_.ac=false;_.cc=1;_.dc=0;_.ec=false;_.fc=false;_.jc=false;_.lc=false;_.mc= +true;_.rc=false;_.sc=false;_.tc=false;_.uc=false;_.vc=false;_.wc=false;_.xc=false;_.yc=false;_.zc=false;_.Cc=false;_.Dc=false;_.Ec=false;_.Fc=false;_.Gc=false;_.Hc=true;_.Ic=false;_.Jc=false;_.Kc=false;_.Lc=false;_.Mc=false;_.Nc=false;_.Qc=true;_.Rc=false;_.Wc=false;_.Xc=true;_.Yc=false;_.Zc=false;_._c=false;_.ad=false;_.bd=false;_.ld=1;_.qd=false;_.td=true;_.ud=false;_.xd=true;var e9,f9;var RL=krc(165);IZ(188,12,{188:1,3:1,20:1,12:1},D9);var y9,z9,A9,B9;var DL=lrc(188,XV,E9);IZ(262,12,{262:1,3:1, +20:1,12:1},I9);var F9,G9;var EL=lrc(262,XV,J9);IZ(187,12,{187:1,3:1,20:1,12:1},P9);var K9,L9,M9,N9;var FL=lrc(187,XV,Q9);IZ(225,12,{225:1,3:1,20:1,12:1},V9);var R9,S9,T9;var GL=lrc(225,XV,W9);IZ(224,12,{224:1,3:1,20:1,12:1},_9);var X9,Y9,Z9;var HL=lrc(224,XV,aab);IZ(263,12,{263:1,3:1,20:1,12:1},eab);var bab,cab;var IL=lrc(263,XV,fab);IZ(89,12,{89:1,3:1,20:1,12:1},xab);var gab,hab,iab,jab,kab,lab,mab,nab,oab,pab,qab,rab,sab,tab;var JL=lrc(89,XV,yab);IZ(687,1,{},zab);var KL=krc(687);IZ(226,12,{226:1, +3:1,20:1,12:1},Eab);var Aab,Bab,Cab;var LL=lrc(226,XV,Fab);IZ(223,12,{223:1,3:1,20:1,12:1},Kab);var Gab,Hab,Iab;var ML=lrc(223,XV,Lab);IZ(222,12,{222:1,3:1,20:1,12:1},Qab);var Mab,Nab,Oab;var NL=lrc(222,XV,Rab);IZ(169,12,{169:1,3:1,20:1,12:1},Yab);var Sab,Tab,Uab,Vab,Wab;var OL=lrc(169,XV,Zab);IZ(227,12,{227:1,3:1,20:1,12:1},cbb);var $ab,_ab,abb;var PL=lrc(227,XV,dbb);IZ(199,25,nMc,fbb);var QL=krc(199);IZ(167,1,EOc);_.th=function gbb(a){return false};_.uh=function hbb(a){return false};_.vh=function ibb(){return(oDb(), +hDb).a};var tQ=krc(167);IZ(218,167,{218:1,167:1,3:1},pbb);_.th=function qbb(a){return lbb(this,a)};_.uh=function rbb(a){return mbb(this,a)};_.wh=function sbb(a){return nbb(this,a)};_.Bd=function tbb(){var a;return Nc(new Pc(iLc),(a=(new Yvc(this.c.a)).a.He().b.vj(),new ewc(a)))};_.a=false;_.d=0;var UL=krc(218);IZ(705,1,FOc,vbb);_.lf=function wbb(a,b){return ubb(this,a,b)};_.yd=function xbb(a){return this===a};_.nf=function ybb(){return new SBc(this)};var TL=krc(705);IZ(723,1,{},zbb);var VL=krc(723); +var Abb;IZ(454,1,wNc,Jbb);_.b=0;var Cbb,Dbb;var XL=krc(454);IZ(326,1,{326:1,3:1,20:1},Mbb);_.ie=function Nbb(a){return Lbb(this,a)};_.a=0;_.b=0;_.c=0;var WL=krc(326);IZ(193,12,{193:1,3:1,20:1,12:1},Tbb);var Obb,Pbb,Qbb,Rbb;var YL=lrc(193,XV,Ubb);IZ(53,1,{53:1,3:1},Zbb,$bb,_bb,acb,bcb);_.Bd=function dcb(){var a;return this.a==null?(drc(aM),aM.p+"@"+(a=NKc(this)>>>0,a.toString(16))):"DiagnosticGroup<"+this.a+">"};var Vbb;var aM=krc(53);IZ(450,1,BLc,ecb);_.Od=function fcb(a){return new Zbb(a)};var $L= +krc(450);IZ(130,167,EOc,gcb);_.th=function hcb(a){return this.b==(E2(),C2)&&Xbb(this.a,a)};_.uh=function icb(a){var b,c;if(this.b!=(E2(),C2))for(c=a.b.Vd();c.Id();){b=c.Jd();if(Ybb(this.a,b))return true}return false};_.wh=function jcb(a){return Ybb(this.a,a.g)?this.b:null};_.Bd=function kcb(){return this.a+"("+this.b+")"};var _L=krc(130);var lcb,mcb,ncb,ocb,pcb,qcb,rcb,scb,tcb,ucb,vcb,wcb,xcb,ycb,zcb,Acb,Bcb,Ccb,Dcb,Ecb,Fcb,Gcb;IZ(5,1,{5:1,3:1,20:1},Mcb);_.ie=function Ncb(a){return Asc(this.b,a.b)}; +_.yd=function Ocb(a){return Lcb(this,a)};_.Ad=function Pcb(){return TKc(this.b)};var bM=krc(5);var Qcb,Rcb;IZ(158,12,aPc);var Tcb,Ucb,Vcb;var fM=lrc(158,XV,Ycb);IZ(702,158,aPc,Zcb);var cM=lrc(702,fM,null);IZ(703,158,aPc,$cb);var dM=lrc(703,fM,null);IZ(704,158,aPc,adb);var eM=lrc(704,fM,null);IZ(620,qNc,fPc,jdb);_.wg=function kdb(a,b){Lyb(this.a,a,bdb,CE(xE(dO,1),mLc,22,0,[this]));Lyb(this.a,b,bdb,CE(xE(dO,1),mLc,22,0,[this]))};_.Cg=function ldb(a,b,c){b.n==(Afc(),bdc)?!Ipb(b)&&ddb(this,a,b):b.n== +ffc&&fdb(this,b,c)};var bdb;var mM=krc(620);IZ(991,1,gPc,ndb);_.Ld=function odb(a){return mdb(a)};_.yd=function pdb(a){return this===a};_.Nd=function qdb(a){return mdb(a)};var gM=krc(991);IZ(618,1,fPc,Ddb);_.wg=function Edb(a,b){this.c=new ilb(this.a,a,b);Lyb(this.a,a,rdb,CE(xE(dO,1),mLc,22,0,[this]));Lyb(this.a,b,rdb,CE(xE(dO,1),mLc,22,0,[this]));Kyb(this.a,rdb)};_.Bg=function Fdb(a,b,c){var d,e;if(b.n==(Afc(),Jdc))kwc(this.b,new Kdb(b));else if(b.n==ffc){e=c.n==Ycc?c:c.g;if(e.n!=Ycc&&c.n==Pdc){Uob(a, +b,(Rjb(),Qjb),CE(xE(nW,1),uNc,2,6,["super access with no extends clause"]));return false}Yd(e.n==Ycc,e);d=Sd(twc(this.b));Ywc(d.b,e)}return true};_.Cg=function Gdb(a,b,c){var d;d=twc(this.b);if(!!d&&b==d.a){vwc(this.b);Cdb(this,a,d)}};var rdb;var lM=krc(618);IZ(990,1,rNc,Hdb);_.Bg=function Idb(a,b,c){return _wc(this.a,b,0)==-1&&!(b.n==(Afc(),Jdc)&&!(b.n==Jdc&&E8b(b,(Hbc(),Fac))!=0))};_.Cg=function Jdb(a,b,c){var d,e,f;if(b.n==(Afc(),lfc)){f=F9b((e=(ae(Hsc(uPc,_sc(46))==-1,HMc,uPc),n8b(),new Wbc(rec, +uPc)),e),b);j9b(c,b,f)}else b.n==Uec&&!b.c&&r8b(b,F9b((d=(ae(Hsc(uPc,_sc(46))==-1,HMc,uPc),n8b(),new Wbc(rec,uPc)),d),b))};var hM=krc(990);IZ(619,1,{619:1},Kdb);var iM=krc(619);IZ(988,1,{},Mdb);_.a=false;var kM=krc(988);IZ(1082,1,rNc);_.Bg=function Ndb(a,b,c){return!c||c.n!=(Afc(),Jdc)||b==c.c};var cO=krc(1082);IZ(989,1082,rNc,Odb);_.Cg=function Pdb(a,b,c){this.a.a||b.n==(Afc(),Uec)&&!!b.c&&!_8b(b.c,vPc)&&(this.a.a=true)};var jM=krc(989);IZ(797,1078,fPc,Tdb);_.wg=function Udb(a,b){epb(this.a,a,this); +if(!this.b){Sdb(b)&&u7(this.a,new Gmb(null,null,-1,-1,Qdb,null,CE(xE(nW,1),uNc,2,6,[])));MCb(this.a,rPc)}};_.Bg=function Vdb(a,b,c){ppb();b.n==(Afc(),Jdc)&&iqb(b.g)&&b.n==Jdc&&Gqb(b.c)&&_8b(b.c,rPc)&&(this.b=true);return!this.b};_.b=false;var Qdb;var nM=krc(797);IZ(611,qNc,fPc,_db);_.wg=function aeb(a,b){Lyb(this.b,a,Wdb,CE(xE(dO,1),mLc,22,0,[this,new deb(this)]));Lyb(this.b,b,Wdb,CE(xE(dO,1),mLc,22,0,[this,new deb(this)]))};_.Cg=function beb(a,b,c){b.n==(Afc(),bdc)&&$db(this,b)&&Zdb(this,a,b)};_.a= +0;var Wdb;var qM=krc(611);IZ(612,1,rNc,deb);_.Bg=function eeb(a,b,c){b.n==(Afc(),bdc)&&(Kd(b.n==bdc),b.c.n==rec&&c.n==rec)&&kwc(this.a,new geb(b.c,c.Wh()));return true};_.Cg=function feb(a,b,c){switch(b.n.f){case 96:{Kd(b.n==(Afc(),bdc));if(b.c.n==rec&&c.n==rec){vwc(this.a);j9b(b,b.c,F9b(new J9b(tdc),b.c));w7(this.b.b,b)}}break;case 29:ceb(this,a,b,c)}};var pM=krc(612);IZ(613,1,{613:1},geb);var oM=krc(613);IZ(624,1,fPc,meb);_.wg=function neb(a,b){Lyb(this.c,a,heb,CE(xE(dO,1),mLc,22,0,[this]));Lyb(this.c, +b,heb,CE(xE(dO,1),mLc,22,0,[this]));Kyb(this.c,heb)};_.Bg=function oeb(a,b,c){return true};_.Cg=function peb(a,b,c){b.n.f==101&&jeb(this,b,c)};_.a=false;var heb;var rM=krc(624);IZ(621,qNc,fPc,ueb);_.wg=function veb(a,b){var c,d,e,f,g;g=(iQb(),YPb);for(f=(!b.c?(Cyc(),Cyc(),Byc):new Kbc(b.c)).Vd();f.Id();){e=f.Jd();g=pQb(g,(c=(ppb(),Yd(e.n==(Afc(),Xec),e),H8b(e,(n8b(),V7b))),c?c:YPb))}d=tQb(g,l9(this.a.G));Lyb(this.a,b,qeb,CE(xE(dO,1),mLc,22,0,[this]));jQb(d,(IRb(),ZQb))&&(Rjb(),C6(this.a,APc+Xsc(BPc, +(bFc(),_Ec)),false));jQb(d,EQb)&&(Rjb(),C6(this.a,APc+Xsc(BPc,(bFc(),_Ec)),false));jQb(d,FQb)&&(Rjb(),C6(this.a,APc+Xsc("arrayFromIterator",(bFc(),_Ec)),false));jQb(d,zRb)&&(Rjb(),C6(this.a,APc+Xsc("arrayfromiterable",(bFc(),_Ec)),false));jQb(d,NQb)&&(Rjb(),C6(this.a,APc+Xsc("inherits",(bFc(),_Ec)),false));jQb(d,OQb)&&C6(this.a,CPc,false);jQb(d,$Qb)&&C6(this.a,"es6/generator_engine",false);jQb(d,HQb)&&C6(this.a,"es6/execute_async_generator",false);jQb(d,IQb)&&C6(this.a,"es6/async_generator_wrapper", +false);jQb(d,YQb)&&C6(this.a,"es6/util/makeasynciterator",false)};_.Cg=function web(a,b,c){var d,e,f,g;switch(b.n.f){case 29:e=H8b(b,(Hbc(),ybc));!(!!e&&e.Kh())&&seb(a,b)&&(C6(this.a,zPc,false),f=(ppb(),Gpb(b,new jrb)),g=t3b(l3b(Wqb(this.a,"$jscomp.initSymbol"),CE(xE(HU,1),IMc,7,0,[]))),p8b(f.g,G9b(g,f),f),w7(this.a,g),undefined);break;case 26:d=H8b(b,(Hbc(),ybc));!!d&&d.Kh()||teb(this,a,b);break;case 86:case 87:this.b||Tjb(this.a,b,DPc)}};_.b=false;var qeb;var sM=krc(621);IZ(609,qNc,fPc,zeb);_.wg= +function Aeb(a,b){Lyb(this.a,a,xeb,CE(xE(dO,1),mLc,22,0,[this]));Lyb(this.a,b,xeb,CE(xE(dO,1),mLc,22,0,[this]));Kyb(this.a,xeb)};_.Cg=function Beb(a,b,c){b.n==(Afc(),cfc)&&c9b(b,(Hbc(),kbc),0)};var xeb;var uM=krc(609);IZ(976,1,$Mc,Ceb);_.wg=function Deb(a,b){throw gZ(new ztc("Es6RelativizeImportPaths not implemented"));};var vM=krc(976);IZ(422,qNc,rNc,Ieb);_.Cg=function Jeb(a,b,c){var d;!this.b&&(ppb(),b.n==(Afc(),rec)&&b.Wh().length!=0)&&Geb(this,a,b,false);d=H8b(b,(Hbc(),mbc));!!d&&Heb(this,a,N4b(d))}; +_.b=false;var Eeb;var wM=krc(422);IZ(615,qNc,fPc,Meb);_.wg=function Neb(a,b){Lyb(this.a,b,Keb,CE(xE(dO,1),mLc,22,0,[this]))};_.Cg=function Oeb(a,b,c){var d,e,f,g,h,i,j,k,l,m,n,o;if(b.n!=(Afc(),Jdc)||(b.c?b.c.i:null).n!=Vcc)return;l=b.c.f;e=new Reb;epb(this.a,l,new Peb(this,e));d=l.f;f=new Ajb(this.a);i=zjb(f,b,Kob(a));h=zjb(f,d,i);m=new fq(new tEc,new iq);for(o=(k=(new fvc(h.e)).a.He().Vd(),new kvc(k));o.a.Id();){n=(g=o.a.Jd(),g.We());j=n.b;kDc(e.a,j)&&!dq(m,h.d,j)&&Vp(m,h.d,j,j+"$"+(""+(new j8(this.a)).a.Q++))}bpb(new dpb(this.a, +new Ieb(m),new Ajb(this.a)),d,d.g,i)};var Keb;var zM=krc(615);IZ(986,1078,rNc,Peb);_.Bg=function Qeb(a,b,c){if(!c)return true;if(c.n==(Afc(),mdc)&&b==(c.c?c.c.i:null)||c.n==fdc&&b==c.c){epb(this.a.a,b,this.b);return false}return true};var xM=krc(986);IZ(985,qNc,rNc,Reb);_.Cg=function Seb(a,b,c){ppb();if(!(b.n==(Afc(),rec)&&b.Wh().length!=0))return;jDc(this.a,b.Wh())};var yM=krc(985);IZ(616,1,fPc,Zeb);_.wg=function $eb(a,b){Lyb(this.a,a,Teb,CE(xE(dO,1),mLc,22,0,[this]));Lyb(this.a,b,Teb,CE(xE(dO,1), +mLc,22,0,[this]));Kyb(this.a,Teb)};_.Bg=function _eb(a,b,c){var d,e;switch(b.n.f){case 85:kwc(this.b,new dfb(b,false));break;case 65:b.n==(Afc(),Jdc)&&E8b(b,(Hbc(),Fac))!=0||kwc(this.b,(e=b.c?b.c.i:null,new dfb(e,mqb(b))));break;case 99:d=Sd(twc(this.b));d.b&&c.n==(Afc(),Ycc)&&c.c==b&&(d.c=Web(c,d.f))}return true};_.Cg=function afb(a,b,c){var d;d=twc(this.b);if(b.n==(Afc(),Jdc)&&E8b(b,(Hbc(),Fac))!=0)Yeb(this,a,b,Sd(d));else if(!!d&&d.f==b){vwc(this.b);Veb(this,a,d)}};var Teb;var CM=krc(616);IZ(418, +1,{418:1},dfb);_.b=false;_.c=null;_.d=false;_.e=false;var AM=krc(418);IZ(987,1,rNc,efb);_.Bg=function ffb(a,b,c){return b.n!=(Afc(),Jdc)||b.n==Jdc&&E8b(b,(Hbc(),Fac))!=0};_.Cg=function gfb(a,b,c){var d;if(b.n==(Afc(),lfc)){cfb(this.a,b.d);d=F9b(r9b((ae(Hsc(EPc,_sc(46))==-1,HMc,EPc),n8b(),new Wbc(rec,EPc)),this.a.g),b);c9b(d,(Hbc(),pbc),1);j9b(b.g,b,d)}else if(b.n==rec&&Esc(b.Wh(),BMc)){bfb(this.a,b.d);d=F9b(r9b((ae(Hsc(FPc,_sc(46))==-1,HMc,FPc),n8b(),new Wbc(rec,FPc)),this.a.a),b);j9b(b.g,b,d)}}; +var BM=krc(987);IZ(626,qNc,fPc,tfb);_.wg=function wfb(a,b){var c;this.d=false;epb(this.a,b,new Dfb(this));epb(this.a,b,this);Lyb(this.a,a,ifb,CE(xE(dO,1),mLc,22,0,[this]));epb(this.a,b,new Ieb(this.c));c=new Ifb(this);epb(this.a,b,c);Hfb(c);rfb(this);Kyb(this.a,ifb)};_.Cg=function xfb(a,b,c){var d,e;if(!b.c||!cqb(b.c))return;Yd(!c||c.n!=(Afc(),Idc),c);(b.n==(Afc(),jec)||b.n==gdc)&&jDc(this.b,b);ppb();if(!!b&&(b.n==ufc||b.n==jec||b.n==gdc))for(e=(!b.c?(Cyc(),Cyc(),Byc):new Kbc(b.c)).Vd();e.Id();){d= +e.Jd();sfb(this,a,b,d)}else{ae(b.n==Jdc||b.n==adc,"Unexpected declaration node: %s",b);sfb(this,a,b,b.c)}};_.d=false;var hfb,ifb;var IM=krc(626);IZ(995,1,gPc,zfb);_.Ld=function Afb(a){return yfb(a)};_.yd=function Bfb(a){return this===a};_.Nd=function Cfb(a){return yfb(a)};var DM=krc(995);IZ(992,qNc,rNc,Dfb);_.Cg=function Efb(a,b,c){b.n==(Afc(),rec)&&!k$(Kob(a),b.Wh())&&jDc(this.a.e,b.Wh())};var EM=krc(992);IZ(993,qNc,rNc,Ifb);_.Cg=function Jfb(a,b,c){var d,e,f,g,h,i,j,k,l,m,n,o;ppb();if(!(b.n==(Afc(), +rec)&&b.Wh().length!=0))return;g=b.Wh();k=Kob(a);o=i$(k,g);if(!o)return;if(z$(o)!=jec&&z$(o)!=gdc)return;(b.g.n==jec||b.g.n==gdc)&&jDc(this.f.b,b.g);d=o.d;f=null;for(m=d;;m=m.b){n=m.d;if(Aqb(n)){f=n;break}else if(!!n.g&&Aqb(n.g)){f=n.g;break}else if(pqb(m.d)||!m.b)return}Vf(this.e,o,b);j=null;for(l=k;l!=d&&l.d!=f;l=l.b)l.d.n==Jdc&&(j=l);if(j){e=j.d;if(Jf(this.a,e,g))return;Vf(this.a,e,g);oEc(this.c,f)||qEc(this.c,f,new Ofb("$jscomp$loop$"+(""+(new j8(this.f.a)).a.Q++)));i=pEc(this.c,f);h="$jscomp$loop$prop$"+ +o.b+"$"+(""+this.f.f.a.Q++);jDc(i.b,o);qEc(this.d,o,h);Vf(this.b,e,i)}};var HM=krc(993);IZ(994,1,rNc,Lfb);_.Bg=function Mfb(a,b,c){Yd(b.n!=(Afc(),bdc),b);if(b.n==Jdc)return false;else if(Aqb(b))if(this.c==null)return false;else{++this.b;return true}else return true};_.Cg=function Nfb(a,b,c){Aqb(b)?--this.b:b.n==(Afc(),hdc)&&(this.b==0&&!b.c?Kfb(this,b):this.c!=null&&!!b.c&&Esc(this.c,(Xd(!!b.c&&!b.c.f),b.c).Wh())&&Kfb(this,b))};_.b=0;_.d=false;var FM=krc(994);IZ(627,1,{627:1},Ofb);var GM=krc(627); +IZ(625,qNc,fPc,Rfb);_.wg=function Sfb(a,b){Lyb(this.a,a,Pfb,CE(xE(dO,1),mLc,22,0,[this]));Lyb(this.a,b,Pfb,CE(xE(dO,1),mLc,22,0,[this]));Kyb(this.a,Pfb)};_.Cg=function Tfb(a,b,c){var d,e,f;b.n==(Afc(),Jdc)&&!!c&&c.n==Vcc&&c.g.n!=Jdc&&(d=b.c,e=d.Sh(false),f=F9b(r3b(e,jec),b),qpb(a.f,(IRb(),fRb)),d._h(""),w7(this.a,d),e9b(c,b),r8b(c,f),w7(this.a,f),r8b(e,b),undefined)};var Pfb;var JM=krc(625);IZ(614,1,fPc,ggb);_.wg=function hgb(a,b){Lyb(this.a,a,Xfb,CE(xE(dO,1),mLc,22,0,[this]));Lyb(this.a,b,Xfb,CE(xE(dO, +1),mLc,22,0,[this]));Kyb(this.a,Xfb)};_.Bg=function igb(a,b,c){switch(b.n.f){case 86:case 87:if(kQb((iQb(),YPb),l9(this.a.G))){Tjb(this.a,b,DPc);return false}break;case 122:Ujb(this.a,b,QNc)}return true};_.Cg=function jgb(a,b,c){b.n.f==96&&dgb(this,a,b,c)};var Ufb,Vfb,Wfb,Xfb;var SM=krc(614);IZ(984,qNc,rNc,kgb);_.Cg=function lgb(a,b,c){if(b.n!=(Afc(),Ccc)||b.c==this.a)return;$8b(this.a,b.c)&&u7(this.b.a,Lmb(b,(Yfb(),Ufb),CE(xE(nW,1),uNc,2,6,[])))};var KM=krc(984);IZ(255,12,{255:1,3:1,20:1,12:1},qgb); +var mgb,ngb,ogb;var OM=lrc(255,XV,rgb);IZ(307,1,{},tgb);var QM=krc(307);IZ(610,qNc,fPc,ygb);_.wg=function zgb(a,b){Lyb(this.b,a,ugb,CE(xE(dO,1),mLc,22,0,[this]));Lyb(this.b,b,ugb,CE(xE(dO,1),mLc,22,0,[this]))};_.Cg=function Agb(a,b,c){var d,e,f,g;b.n==(Afc(),bdc)&&(Kd(b.n==bdc),d=b.c.f,d.n!=tdc&!V8b(d))&&(wgb(this,b)?xgb(this,a,b):(e=new J9b(Vcc),f=v3b((ae(Hsc("",_sc(46))==-1,HMc,""),n8b(),new Wbc(rec,"")),new J9b(Jec),e),g=Tqb(f,CE(xE(HU,1),IMc,7,0,[])),j9b(b.g,b,g),q8b(e,(Xd(H3b(b)),new L9b(Uec, +b))),I9b(g,b),w7(a.c,g),xgb(this,a,b),undefined))};_.a=0;var ugb;var RM=krc(610);IZ(522,1,fPc,Ogb);_.wg=function Pgb(a,b){Xd(swc(this.e));Lyb(this.a,a,this.d,CE(xE(dO,1),mLc,22,0,[this]));Lyb(this.a,b,this.d,CE(xE(dO,1),mLc,22,0,[this]));Kyb(this.a,this.c);Xd(swc(this.e))};_.Bg=function Qgb(a,b,c){var d,e,f;switch(b.n.f){case 65:Egb(a,b);break;case 45:Ggb(this,b,c);break;case 93:case 94:{d=b.n==(Afc(),Fec)&&!!b.c&&(b.c?b.c.i:null).n==Sec;if(!swc(this.e)&&d){for(f=new Rwc(this.e);f.a!=f.b;){e=Pwc(f); +if(e.a)break;e.a=true}uwc(this.e).a=true}lwc(this.e,new Zgb(b,d));break}}return true};_.Cg=function Rgb(a,b,c){Lgb(this,a,b,c)};_.b=0;var WM=krc(522);IZ(523,1,{},Tgb);var TM=krc(523);IZ(303,12,{303:1,3:1,20:1,12:1},Xgb);var Ugb,Vgb;var UM=lrc(303,XV,Ygb);IZ(524,1,{524:1},Zgb);_.a=false;var VM=krc(524);IZ(628,1,$Mc,ahb);_.wg=function bhb(a,b){Lyb(this.b,b,$gb,CE(xE(dO,1),mLc,22,0,[new chb(this)]));Kyb(this.b,$gb)};_.g=false;var $gb;var gN=krc(628);IZ(997,1,rNc,chb);_.Bg=function dhb(a,b,c){E8b(b,(Hbc(), +Xac))!=0&&++this.a;return true};_.Cg=function ehb(a,b,c){E8b(b,(Hbc(),Xac))!=0&&lhb(new zhb(this.b,b,--this.a))};_.a=0;var XM=krc(997);IZ(998,1,{},zhb);_.b=0;var dN=krc(998);IZ(633,1,{633:1},Ahb);var YM=krc(633);IZ(1E3,1,{},kib);_.b=false;_.d=0;_.o=0;_.s=false;var bN=krc(1E3);IZ(107,1,{107:1},pib);_.c=0;_.e=true;var ZM=krc(107);IZ(630,1,{630:1},qib);_.b=0;var $M=krc(630);IZ(631,1,{631:1},rib);var _M=krc(631);IZ(629,1,rNc,wib);_.Bg=function xib(a,b,c){var d;if(E8b(b,(Hbc(),hbc))!=0){c9b(b,hbc,0);return false}Xd(E8b(b, +gbc)==0);Yd(b.n!=(Afc(),ffc),"Reference to SUPER is not supported");if(Aqb(b)){++this.b;++this.a}else b.n==gfc&&++this.a;if(b.n==Xcc||b.n==hdc){b.c?uib(this,b):(d=null,b.n==Xcc&&this.a==0&&(d=qwc(this.c.c)),b.n==hdc&&this.b==0&&(d=qwc(this.c.i)),!!d&&_hb(this.c.r.a,b,d,this.a),undefined);return false}return b.n!=Jdc};_.Cg=function yib(a,b,c){var d,e,f,g;if(Aqb(b)){--this.b;--this.a}else b.n==(Afc(),gfc)?--this.a:b.n==lfc?vib(this,b):b.n==Uec?r8b(b,bib(this.c,b,g9b(b))):b.n==rec&&Esc(b.Wh(),BMc)?tib(this, +b):b.n==ufc&&(c.n==Fdc?(d=sib(b),!d?k9b(b,new J9b(tdc)):j9b(b.g,b,d),ghb(this.c.r,b),undefined):c.n==Hdc?(e=(Xd(!!b.c&&!b.c.f),b.c),Yd(!e.c,e),f=q9b(e.Sh(false),null),j9b(b.g,b,f),ghb(this.c.r,b),undefined):(g=sib(b),!g?z8b(b):k9b(b,(Yd(H3b(g),g),new L9b(Ddc,g))),ghb(this.c.r,b),undefined))};_.a=0;_.b=0;var aN=krc(629);IZ(999,1078,rNc,zib);_.Bg=function Aib(a,b,c){if(b.n==(Afc(),Jdc))return false;if(b.n==zfc){Xd(!this.a);this.a=b;return false}return true};var cN=krc(999);IZ(996,1078,rNc,Cib);_.Bg= +function Dib(a,b,c){c9b(b,(Hbc(),gbc),0);if(b.n==(Afc(),Jdc))return false;if(b.n==zfc){Bib(this,b);return false}return true};var eN=krc(996);IZ(632,1,rNc,Eib);_.Bg=function Fib(a,b,c){return b.n!=(Afc(),Jdc)};_.Cg=function Gib(a,b,c){b.n==(Afc(),zfc)&&c9b(b,(Hbc(),gbc),1);!!c&&E8b(b,(Hbc(),gbc))!=0&&c9b(c,(Hbc(),gbc),1)};var fN=krc(632);IZ(602,1,$Mc,Jib);_.wg=function Lib(a,b){var c,d;for(d=(!b.c?(Cyc(),Cyc(),Byc):new Kbc(b.c)).Vd();d.Id();){c=d.Jd();if(Iib(c)){epb(this.a,c,new bjb(this,this.a,c)); +b9b(c,(n8b(),l8b),true)}}z7(this.a,sQb(this.a.p,(IRb(),iRb),CE(xE(FR,1),sLc,24,0,[])))};var kN=krc(602);IZ(210,1,{210:1},Mib);var hN=krc(210);IZ(975,qNc,rNc,bjb);_.Cg=function cjb(a,b,c){Wib(this,a,b,c)};var jN=krc(975);IZ(622,qNc,fPc,ojb);_.wg=function pjb(a,b){Lyb(this.c,a,ejb,CE(xE(dO,1),mLc,22,0,[this]));Lyb(this.c,b,ejb,CE(xE(dO,1),mLc,22,0,[this]));Kyb(this.c,ejb)};_.Cg=function qjb(a,b,c){var d,e;switch(b.n.f){case 114:njb(this,a,b,c);break;case 42:case 23:case 28:for(e=(!b.c?(Cyc(),Cyc(), +Byc):new Kbc(b.c)).Vd();e.Id();){d=e.Jd();if(d.n==(Afc(),_ec)){ljb(this,b);break}}}};var djb,ejb;var lN=krc(622);IZ(617,qNc,fPc,ujb);_.wg=function vjb(a,b){Lyb(this.a,b,rjb,CE(xE(dO,1),mLc,22,0,[this]))};_.Cg=function wjb(a,b,c){jqb(b)&&tjb(a,b,c)};var rjb;var mN=krc(617);IZ(122,1,{},Ajb,Bjb);var xjb;var pN=krc(122);IZ(755,1,{},Cjb);_.xh=function Djb(a,b,c,d){};var nN=krc(755);IZ(754,1,{},Ijb);var oN=krc(754);var Pjb,Qjb;IZ(608,1,fPc,_jb);_.wg=function akb(a,b){Lyb(this.a,a,Zjb,CE(xE(dO,1),mLc,22, +0,[this]));Lyb(this.a,b,Zjb,CE(xE(dO,1),mLc,22,0,[this]));Kyb(this.a,Zjb)};_.Bg=function bkb(a,b,c){return true};_.Cg=function ckb(a,b,c){var d,e,f,g;switch(b.n.f){case 18:d=x8b(this.b,false);q8b(d,g9b(b));q8b(d,g9b(b));I9b(d,b);j9b(b.g,b,d);w7(this.a,d);break;case 59:e=g9b(b);f=x8b(this.b,false);q8b(f,x8b(e,false));q8b(f,g9b(b));g=r9b(h3b(e,f),this.e);I9b(g,b);j9b(b.g,b,g);w7(this.a,g)}};var Zjb;var qN=krc(608);IZ(639,1,{},okb);_.a=false;_.g="JSCompiler_temp";var sN=krc(639);IZ(1017,1,{},vkb);_.Bd= +function wkb(){return Cd(Ad(Ad(new Dd((drc(rN),rN.n)),"sideEffects",""+this.b),"extractBeforeStatement",this.a))};_.b=false;var rN=krc(1017);IZ(776,167,EOc);var tN=krc(776);IZ(677,1,aQc,Ckb);_.yh=function Dkb(a){};_.zh=function Ekb(a){Kob(a)==this.b&&(this.b=null)};_.Bg=function Fkb(a,b,c){this.d&&b.n==(Afc(),Jdc)&&Ktb(b,this.a.G.$b)&&!this.b&&(this.b=Kob(a));return true};_.Cg=function Gkb(a,b,c){var d,e,f,g,h,i,j,k,l;i=this.a.G.$b;if(!c||gqb(c)||(ppb(),c.n==(Afc(),Vec)||c.n==Xec||c.n==Vcc||c.n== +pec))if(b.n==(Afc(),Ddc)){e=b.c.c;if(!!e&&(a9b(e,bQc,bQc.length)||a9b(e,cQc,cQc.length))){this.c=(b9(),Z8);return}}if(this.e&&b.n==(Afc(),Adc)){this.c=(b9(),Y8);if(E8b(b,(n8b(),U7b))!=0);else!!b.c&&!!b.c.f&&b.c.f==(b.c?b.c.i:null)&&ykb(a,b)}else if(this.e&&b.n==(Afc(),Vdc)){this.c=(b9(),Y8);ykb(a,b)}else if(this.d)if(this.c!=(b9(),Z8)&&Ltb(a,b))this.c=X8;else if(Mtb(b,i)){h=Jtb(b,i);f=oFb(z8((!a.d&&!!a.i&&(a.d=L6(a.c,a.i)),a.d)),h,(d=H8b(b,(Hbc(),ybc)),!d?null:d.ug()),T9b(b.k),S9b(b.k));!!f&&(!!this.b|| +Ktb((ppb(),Gpb(b,new frb((Afc(),Jdc)))),i)?t8((!a.d&&!!a.i&&(a.d=L6(a.c,a.i)),a.d),UFb(f.a)):u8((!a.d&&!!a.i&&(a.d=L6(a.c,a.i)),a.d),yDb(UFb(f.a),h)))}if(!!c&&(c.n==(Afc(),Ddc)||!(j=(k=twc(a.b),pF(k,7)?l=k:l=k.Jj.Jj,l),Yd(j.n==Xec||j.n==Vec||j.n==Vcc||j.n==Jdc||j.n==pec,j),j.n==Xec||j.n==Vec||j.n==Vcc))&&b.n==(Afc(),Ycc)&&_8b(b.c,"goog.require")&&!!b.c.f&&b.c.f.n==(Afc(),bfc)){g=b.c.f.Wh();Esc(g.substr(0,5),"goog.")&&u8((!a.d&&!!a.i&&(a.d=L6(a.c,a.i)),a.d),(wDb(),vDb));u8((!a.d&&!!a.i&&(a.d=L6(a.c, +a.i)),a.d),(wDb(),GDb(JDb(IDb(HDb(new KDb,g),g),(MEb(),KEb)))))}};_.b=null;_.d=false;_.e=false;var vN=krc(677);IZ(869,1078,rNc,Hkb);_.Bg=function Ikb(a,b,c){var d;if(this.a)return false;if(!c||gqb(c)||(ppb(),c.n==(Afc(),Vec)||c.n==Xec||c.n==Vcc||c.n==pec)){if(b.n==(Afc(),Ddc)){d=b.c.c;if(!!d&&(a9b(d,bQc,bQc.length)||a9b(d,cQc,cQc.length))){this.a=true;return false}}return true}return false};_.a=false;var uN=krc(869);IZ(941,1,dQc,Jkb);_.Jg=function Kkb(){throw gZ(new Irc("Code changes forbidden")); +};var wN=krc(941);var Lkb,Mkb,Nkb,Okb,Pkb,Qkb,Rkb,Skb,Tkb,Ukb,Vkb,Wkb,Xkb,Ykb,Zkb;IZ(950,qNc,fPc,dlb);_.wg=function elb(a,b){clb(this,a)};_.Cg=function flb(a,b,c){var d,e;switch(b.n.f){case 26:d=b.c.f;d.n==(Afc(),bfc)&&jDc(this.b,d.Wh());break;case 91:c.n==(Afc(),Eec)&&jDc(this.b,b.Wh());break;case 98:jDc(this.b,b.Wh())}e=H8b(b,(Hbc(),mbc));!!e&&_kb(this,e)};var xN=krc(950);IZ(924,1,{},ilb);_.b=0;_.e=false;var DN=krc(924);IZ(518,1078,rNc,zlb);_.Bg=function Alb(a,b,c){var d,e;!!this.a.c&&(b==this.a.c? +this.a.d=Kob(a):b.n==(Afc(),Xec)&&(d=H8b(b,(Hbc(),ybc)),!!d&&d.Kh()?oqb(b)?1:0:2));llb(this,(e=(!a.d&&!!a.i&&(a.d=L6(a.c,a.i)),a.d),!e?null:e.k),Kob(a),b);return true};var yN=krc(518);IZ(381,1,{381:1,3:1},Ilb);_.Bd=function Klb(){return Flb(this)+" ("+this.t+"): "+Nc(new Pc(iLc),new Guc(Qc("globalSets="+this.i,"localSets="+this.j,CE(xE(iW,1),mLc,1,5,["totalGets="+this.s,"aliasingGets="+this.a,"callGets="+this.c,"subclassingGets="+this.r]))))};_.a=0;_.c=0;_.e=0;_.f=null;_.g=null;_.i=0;_.j=0;_.k=0; +_.r=0;_.s=0;var AN=krc(381);IZ(163,12,{163:1,3:1,20:1,12:1},Slb);var Llb,Mlb,Nlb,Olb,Plb,Qlb;var zN=lrc(163,XV,Tlb);IZ(299,1,{299:1},Ulb);_.Bd=function Vlb(){return Cd(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Bd(new Dd((drc(CN),CN.n))),"name",this.b),$Nc,this.g),"node",this.c),"preOrderIndex",""+this.d),"isTwin",""+!!this.f),mNc,this.a),"scope",this.e))};_.d=0;_.f=null;var CN=krc(299);IZ(139,12,{139:1,3:1,20:1,12:1},dmb);var Wlb,Xlb,Ylb,Zlb,$lb,_lb,amb,bmb;var BN=lrc(139,XV,emb);IZ(799,1,rNc);_.Bg=function mmb(a,b,c){var d, +e;if(!c)kwc(this.d,(pmb(),omb));else{kwc(this.d,qmb(twc(this.d),this.c,c,b));if(!!c&&fmb.Sd(c.n)&&Tf(this.g,c))for(e=Wf(this.g,c).Vd();e.Id();){d=e.Jd();lj(this.e,d,1);Vf(this.f,c,d)}}return true};_.Cg=function nmb(a,b,c){!!c&&fmb.Sd(b.n)&&Tf(this.f,b)&&$i(this.e,Wf(this.f,b));(b.n==(Afc(),Uec)||b.n==mfc)&&jmb(this,c);Rvb(this,a,b);vwc(this.d)};var fmb,gmb;var FN=krc(799);IZ(126,1,{126:1},smb);_.c=false;var omb;var EN=krc(126);IZ(651,437,{});var GN=krc(651);var tmb;var vmb;IZ(371,1,{},Emb);_.a=false; +var HN=krc(371);IZ(61,1,{61:1,3:1},Gmb,Hmb);_.yd=function Imb(a){var b;if(this===a)return true;if(a==null||IN!=Bb(a))return false;b=a;if(this.a!=b.a)return false;if(this.d!=b.d)return false;if(!Esc(this.c,b.c))return false;if(this.b!=b.b)return false;if(this.f!=null?!Esc(this.f,b.f):b.f!=null)return false;return Lcb(this.g,b.g)};_.Ad=function Jmb(){var a;a=TKc(this.g.b);a=31*a+TKc(this.c);a=31*a+(this.f!=null?TKc(this.f):0);a=31*a+this.d;a=31*a+NKc(this.b);a=31*a+this.a;return a};_.Bd=function Nmb(){return this.g.b+ +". "+this.c+" at "+(this.f!=null&&this.f.length>0?this.f:"(unknown source)")+" line "+(this.d!=-1?""+this.d:"(unknown line)")+" : "+(this.a!=-1?""+this.a:"(unknown column)")};_.a=0;_.d=0;var IN=krc(61);IZ(257,239,{257:1,431:1,239:1,3:1},Smb);_.lh=function Tmb(){throw gZ(new ytc);};_.ug=function Umb(){return this.e};_.mh=function Vmb(){return Bn(),new Wz(Sd(this.e))};_.oh=function Wmb(){var a,b,c;a=(Bn(),new Zr);for(c=new wxc(this.a);c.a0&&KIc(this.b,a,x2b("%d error(s), %d warning(s)",CE(xE(iW,1),mLc,1,5,[Yrc(this.d+this.e),Yrc(this.f)])))};_.zg=function _nb(a,b){switch(a.f){case 0:OIc(this.b,Fmb(b,a,this.a));break;case 1:PIc(this.b,Fmb(b,a,this.a))}};var VN=krc(660);IZ(1083,1,aQc);_.yh=function aob(a){}; +_.zh=function bob(a){};_.Bg=function cob(a,b,c){return true};var bO=krc(1083);IZ(260,1083,aQc,gob,hob);_.yh=function job(a){var b,c,d;ae(true,"MakeDeclaredNamesUnique requires an ES6-compatible scope creator. %s is not compatible.",a.k);b=Nob(a);if(swc(this.b)){Xd(b.n!=(Afc(),Jdc)||!this.c);d=this.c}else{c=b.n!=(Afc(),Jdc)&&!ypb(b);d=twc(this.b).Dh(Nob(a),c)}kwc(this.b,d);dob(this,a,b)};_.zh=function kob(a){var b;Mob(a)==0||(b=vwc(this.b),b)};_.Cg=function lob(a,b,c){var d,e;switch(b.n.f){case 29:case 108:fob(this, +a,b,c);break;case 91:{e=eob(this,b.Wh());if(e!=null&&!b.c){d=F9b(K3b(b.Wh()),b);q8b(b,d);fob(this,a,d,b)}break}}};_.a=false;var ZN=krc(260);IZ(408,1,wQc,nob,oob);_.Ch=function pob(a,b){var c,d;if(b&&this.f!=this)this.f.Ch(a,true);else if(!Esc(a,BMc))if(this.e)Qy(this.g,a,0,1);else if(!Ztc(this.d,a)){c=lj(this.g,a,1);d=null;c!=0&&(d=a+_Pc+c);_tc(this.d,a,d)}};_.Dh=function qob(a,b){return new oob(a,this.g,b,this)};_.Eh=function rob(){return this.f};_.Fh=function sob(a){return Ytc(this.d,a)};_.Gh=function tob(){return false}; +_.Bd=function uob(){return Cd(Ad(Ad(Ad(Ad(new Dd(grc(this.Fj)),"scopeRoot",this.i),"nameUsage",this.g),"declarations",this.d),vNc,""+this.e))};_.e=false;var XN=krc(408);IZ(679,408,wQc,vob);_.Dh=function wob(a,b){return new yob(this.a,this.c,this.b,false,b,this)};var WN=krc(679);IZ(596,1,wQc,yob);_.Ch=function zob(a,b){Xd(!Esc(a,BMc));b&&this.c!=this?this.c.Ch(a,b):Ztc(this.b,a)||_tc(this.b,a,xob(this,a))};_.Dh=function Aob(a,b){return new yob(this.a,this.f,this.d,this.e,b,this)};_.Eh=function Bob(){return this.c}; +_.Fh=function Cob(a){return Ytc(this.b,a)};_.Gh=function Dob(){return this.e};_.e=false;var YN=krc(596);var Eob,Fob,Gob;IZ(155,1,{},dpb);_.q=false;var eO=krc(155);var gpb,hpb,ipb,jpb,kpb,lpb,mpb,npb,opb;IZ(147,1,gPc,frb);_.Ld=function grb(a){return a.n==this.a};_.yd=function hrb(a){return this===a};_.Nd=function irb(a){return a.n==this.a};var fO=krc(147);IZ(115,1,gPc,jrb);_.yd=function lrb(a){return this===a};_.Nd=function mrb(a){return Iqb(a)};_.Ld=function krb(a){return Iqb(a)};var gO=krc(115); +IZ(743,1,gPc,nrb);_.Ld=function orb(a){return a.n==this.a};_.yd=function prb(a){return this===a};_.Nd=function qrb(a){return a.n==this.a};var hO=krc(743);IZ(744,1,gPc,rrb);_.yd=function trb(a){return this===a};_.Nd=function urb(a){return ppb(),a.n!=(Afc(),Jdc)};_.Ld=function srb(a){return ppb(),a.n!=(Afc(),Jdc)};var iO=krc(744);IZ(745,1,gPc,vrb);_.yd=function xrb(a){return this===a};_.Nd=function yrb(a){return ppb(),!Pqb(a)};_.Ld=function wrb(a){return ppb(),!Pqb(a)};var jO=krc(745);IZ(942,1,$Mc, +Brb);_.wg=function Crb(a,b){zrb(this,a,b)};_.a=false;var sO=krc(942);IZ(948,1,{},Erb);_.xh=function Frb(a,b,c,d){var e,f;Xd(c.n==(Afc(),rec));e=c.g;f=i$(a,b);if(!a.b)if((!f.a||B8(f.a))&&!B8(d))if(jDc(this.a,f))return;if(e.n==Jdc){if((!f.c?null:f.c.g).n==ufc){l$(a,f);twb(a,b,c,f.a);Drb(this,f.c,!f.c?null:f.c.g,(!f.c?null:f.c.g).g)}}else if(e.n==ufc){Xd(!!e.c&&!e.c.f);Drb(this,c,e,e.g)}};var kO=krc(948);IZ(944,qNc,rNc,Grb);_.Cg=function Hrb(a,b,c){var d,e,f,g,h;ppb();if(b.n==(Afc(),Ddc)&&b.c.n==Ccc){d= +b.c;h=d.c;h.n==Pdc&&(f=H8b(d,(Hbc(),mbc)),!!f&&(f.a&CQc)!=0)&&jDc(this.a,(h.c?h.c.i:null).Wh())}else b.n==cfc&&(g=H8b(b,(Hbc(),mbc)),!!g&&(g.a&CQc)!=0)?jDc(this.a,b.Wh()):b.n==Pdc&&b.g.n==Ddc&&(e=H8b(b,(Hbc(),mbc)),!!e&&(e.a&CQc)!=0)&&jDc(this.a,(b.c?b.c.i:null).Wh())};var lO=krc(944);IZ(447,1,rNc,Qrb);_.Bg=function Rrb(a,b,c){b.n==(Afc(),eec)&&Mrb(this,b);ppb();(b.n==Vec||b.n==Xec||b.n==Vcc||b.n==pec||b.n==eec)&&Jrb(this,b,null,null);(b.n==Vec||b.n==Xec||b.n==Vcc||b.n==pec)&&Prb(this,b);b.n==Jdc&& +Krb(this,b.c?b.c.i:null);aqb(b)&&b.n!=Ccc&&Lrb(this,b);return true};_.Cg=function Srb(a,b,c){var d,e;switch(b.n.f){case 70:e=b.c;A9b(b,(Afc(),Fdc));d=new J9b(tdc);H9b(d,b);p8b(b,d,e);o8b(b,y8b(d,new J9b(d.n),false),e);Nrb(this,"WHILE node",b);break;case 65:Trb(b,this.b)&&Nrb(this,"Function declaration",b);break;case 109:Orb(this,b);break;case 29:case 31:case 86:case 87:Irb(this,b,c);break;case 92:w7(this.b,b);j9b(c,b,g9b(b))}};_.a=false;var mO=krc(447);IZ(946,qNc,fPc,Vrb);_.wg=function Wrb(a,b){Urb(this, +a,b)};_.Cg=function Xrb(a,b,c){var d,e,f,g,h,i;if(b.n==(Afc(),rec)||b.n==cfc){if(b.Wh().length==0)return;d=null;i=i$(Kob(a),b.Wh());!!i&&(d=!i.c?null:(ppb(),f=Dpb(i.c),!f?null:H8b(f,(Hbc(),mbc))));h=!!d&&((d.a&37)!=0||(!d.d?null:d.d.d)!=null)||eqb(H6(this.b),b);e=E8b(b,(n8b(),a8b))!=0;if(h&&!e){if(this.a){g=b.Wh();throw gZ(new Irc(DQc+g+gMc+" parent:"+E9b(b.g)));}b9b(b,a8b,true)}}};_.a=false;var nO=krc(946);IZ(943,qNc,rNc,Yrb);_.Cg=function Zrb(a,b,c){if(b.n==(Afc(),tdc)&&c.n==cdc){Arb(this.a,"empty member in class", +b);z8b(b)}};var oO=krc(943);IZ(945,qNc,rNc,$rb);_.Cg=function _rb(a,b,c){var d,e,f;if(b.n==(Afc(),Pdc)){f=(b.c?b.c.i:null).Wh();if(kDc(this.a,f)){d=g9b(b);e=g9b(b);w7(this.b.b,b);k9b(b,w3b(d,e))}}else if(b.n==cfc){f=b.Wh();if(kDc(this.a,f))if(!b.Yh()){w7(this.b.b,b);b.$h()}}};var pO=krc(945);IZ(949,1,aQc,asb);_.yh=function bsb(a){Kob(a)};_.zh=function csb(a){};_.Bg=function dsb(a,b,c){return true};_.Cg=function esb(a,b,c){};var qO=krc(949);IZ(947,qNc,fPc,gsb);_.wg=function hsb(a,b){fsb(this,a,b)}; +_.Cg=function isb(a,b,c){var d,e,f,g,h,i,j,k;if(b.n==(Afc(),rec)){i=b.Wh();if(b.Wh().length==0)return;g=E8b(b,(n8b(),a8b))!=0;if(this.a){e=false;d=H6(this.b);ppb();if(E8b(b,a8b)!=0||eqb(d,b))e=true;else{e=false;f=null;k=i$(Kob(a),b.Wh());!!k&&(f=!k.c?null:(h=Dpb(k.c),!h?null:H8b(h,(Hbc(),mbc))));!!f&&((f.a&37)!=0||(!f.d?null:f.d.d)!=null)?e=true:e=false}e?ae(e==g,"The name %s is not annotated as constant.",i):ae(e==g,"The name %s should not be annotated as constant.",i)}j=Ytc(this.c,i);j==null?_tc(this.c, +i,(Nqc(),g?true:false)):ae(j==g,"The name %s is not consistently annotated as constant.",i)}};_.a=false;var rO=krc(947);IZ(857,1,{});var tO=krc(857);IZ(64,47,nOc);_.ih=function ksb(a){return this.Hh(a)};var uO=krc(64);var lsb,msb,nsb;IZ(672,1,{},ysb);_.a=0;_.b=-1;_.c=0;_.e=-1;_.g=0;_.i=0;_.j=0;_.k=0;_.o=-1;_.p=0;_.q=-1;_.r=-1;_.s=-1;_.t=0;_.v=0;_.A=0;_.B=0;_.C=0;_.F=0;_.G=0;_.H=0;var zO=krc(672);IZ(753,1,EQc,zsb);_.lf=function Asb(a,b){return asc(a.We().n,b.We().n)};_.yd=function Bsb(a){return this=== +a};_.nf=function Csb(){return new SBc(this)};var wO=krc(753);IZ(347,1,{347:1},Dsb);_.a=0;_.b=0;_.c=0;_.d=0;_.e=0;_.f=0;_.g=0;_.i=false;_.k=0;_.n=0;_.o=0;var xO=krc(347);IZ(784,480,{},Esb);_.Lg=function Fsb(a){var b;b=a.length;if(b>0){this.b+=b;this.a=a.charCodeAt(b-1)}};_.Xg=function Hsb(){return this.a};_.a=0;_.b=0;var yO=krc(784);IZ(667,1,$Mc,Tsb);_.wg=function Usb(a,b){Qsb(this,a,b)};_.c=false;_.e=0;_.g=0;_.j=false;_.k=0;_.o=0;_.q=false;var Isb,Jsb,Ksb,Lsb;var EO=krc(667);IZ(471,1,$Mc,Ysb);_.wg= +function Zsb(a,b){var c,d,e,f,g,h,i,j,k,l;Yd(!this.f.c,"Nested loops are forbidden");this.f.c=true;Xsb(this);this.b=Wsb(this);this.e=new etb(this.f);w6(this.f.b,this.e);this.f.f=new fDc;for(i=new wxc(this.d);i.athis.f.g&&this.b)return;d>100&&C7("Fixed point loop exceeded the maximum number of iterations.",null);++d;e=false;for(h=new wxc(this.d);h.athis.d.length)throw gZ(new qnb("Expected line number between 1 and "+this.d.length+"\nActual: "+ +a));return this.d[a-1]};_.ug=function Ywb(){return this.b};_.Bd=function $wb(){return this.b};_.a=null;_.d=null;var uP=krc(189);IZ(688,1,{},bxb);var sP=krc(688);IZ(689,189,dRc,cxb);var tP=krc(689);IZ(676,qNc,rNc,dxb);_.Cg=function fxb(a,b,c){var d,e,f,g;this.a&&this.b!=null&&Xd(Esc(this.b,(d=H8b(b,(Hbc(),ybc)),!d?null:d.ug())));switch(b.n.f){case 26:g=b.c?b.c.i:null;exb(b,g.Wh());break;case 65:e=Qpb(b);e!=null&&e.length!=0&&H8b(b,(Hbc(),sbc))==null&&d9b(b,(Hbc(),sbc),e);break;case 29:exb(b,b.Wh()); +break;case 43:for(f=b.c;f;f=f.f)f.n!=(Afc(),fdc)&&!f.Yh()&&f.n!=_ec&&exb(f,f.Wh())}};_.a=false;var vP=krc(676);IZ(195,12,eRc);_.Nd=function kxb(a){return this.Ld(a)};var gxb,hxb;var yP=lrc(195,XV,lxb);IZ(708,195,eRc,mxb);_.Ld=function nxb(a){return true};var wP=lrc(708,yP,null);IZ(709,195,eRc,oxb);_.Ld=function pxb(a){return a.n==(Afc(),Ycc)||a.n==xec||a.n==Jdc||a.n==rec||(ppb(),a.n==Pdc||a.n==Odc)||Sqb(a)||a.n==bfc&&qqb(a.g)||a.n==hfc};var xP=lrc(709,yP,null);IZ(194,12,fRc);var qxb,rxb;var BP=lrc(194, +XV,uxb);IZ(706,194,fRc,vxb);var zP=lrc(706,BP,null);IZ(707,194,fRc,wxb);var AP=lrc(707,BP,null);IZ(474,1,{474:1,3:1},Bxb);_.a=false;_.b=null;var xxb,yxb;var CP=krc(474);var Dxb,Exb,Fxb,Gxb,Hxb,Ixb,Jxb,Kxb,Lxb,Mxb,Nxb,Oxb;IZ(661,776,EOc,Qxb);_.vh=function Rxb(){return(oDb(),nDb).a};_.wh=function Sxb(a){var b,c,d,e,f,g,h,i,j,k;h=a.e;!h&&(h=xkb(this,a));if(h)for(b=h;b;b=b.g){d=null;b.n==(Afc(),Jdc)||b.n==bdc?d=(ppb(),f=Dpb(b),!f?null:H8b(f,(Hbc(),mbc))):b.n==Xec?d=H8b(b,(Hbc(),mbc)):(ppb(),(!!b&&(b.n== +ufc||b.n==jec||b.n==gdc)||b.n==Ccc&&b.g.n==Ddc||b.n==Pdc&&b.g.n==Ddc||Sqb(b)||b.n==fdc)&&(d=(e=Dpb(b),!e?null:H8b(e,(Hbc(),mbc)))));if(d)for(k=(i=!d.d?null:d.d.q,!i?(Cyc(),Cyc(),Byc):i).Vd();k.Id();){j=k.Jd();c=Ytc(this.a,j);if(c){g=c.wh(a);if(g)return g}}}return null};var DP=krc(661);IZ(671,1,wNc,Txb);_.qh=function Uxb(a){return this.b};_.rh=function Vxb(){return this.a};_.sh=function Wxb(){return this.c};var EP=krc(671);IZ(659,1,{},Yxb);_.xg=function Zxb(a,b){Xxb(this,a,b)};var FP=krc(659);IZ(439, +1,{},ayb);var IP=krc(439);IZ(349,1,{349:1},cyb);var GP=krc(349);IZ(350,1,{350:1},dyb);_.yd=function eyb(a){if(pF(a,350))return Esc(this.a,a.a);return false};_.Ad=function fyb(){return TKc(this.a)};var HP=krc(350);IZ(184,1,{},hyb);_.a=0;var JP=krc(184);var iyb,jyb,kyb,lyb,myb,nyb,oyb,pyb,qyb,ryb,syb,tyb,uyb,vyb,wyb,xyb,yyb,zyb,Ayb,Byb,Cyb,Dyb,Eyb;IZ(899,47,nOc,Myb);_.ih=function Nyb(a){return new Jib(a)};_.jh=function Oyb(){return iQb(),iQb(),gQb};var UP=krc(899);IZ(908,64,nOc,Pyb);_.ih=function Qyb(a){return new ygb(a)}; +_.Hh=function Ryb(a){return new ygb(a)};_.jh=function Syb(){return iQb(),UPb};var KP=krc(908);IZ(909,64,nOc,Tyb);_.ih=function Uyb(a){return new _db(a)};_.Hh=function Vyb(a){return new _db(a)};_.jh=function Wyb(){return iQb(),cQb};var LP=krc(909);IZ(910,64,nOc,Xyb);_.ih=function Yyb(a){return new ggb(a)};_.Hh=function Zyb(a){return new ggb(a)};_.jh=function $yb(){return iQb(),cQb};var MP=krc(910);IZ(911,64,nOc,_yb);_.ih=function azb(a){return new Meb(a)};_.Hh=function bzb(a){return new Meb(a)};_.jh= +function czb(){return iQb(),UPb};var NP=krc(911);IZ(912,64,nOc,dzb);_.ih=function ezb(a){return new Zeb(a)};_.Hh=function fzb(a){return new Zeb(a)};_.jh=function gzb(){return iQb(),cQb};var OP=krc(912);IZ(913,64,nOc,hzb);_.ih=function izb(a){return new Fvb(a)};_.Hh=function jzb(a){return new Fvb(a)};_.jh=function kzb(){return iQb(),iQb(),gQb};var PP=krc(913);IZ(914,64,nOc,lzb);_.ih=function mzb(a){return new ujb(a)};_.Hh=function nzb(a){return new ujb(a)};_.jh=function ozb(){return iQb(),iQb(),gQb}; +var QP=krc(914);IZ(915,64,nOc,pzb);_.ih=function qzb(a){return new Ddb(a)};_.Hh=function rzb(a){return new Ddb(a)};_.jh=function szb(){return iQb(),cQb};var RP=krc(915);IZ(916,64,nOc,tzb);_.ih=function uzb(a){return new jdb(a)};_.Hh=function vzb(a){return new jdb(a)};_.jh=function wzb(){return iQb(),UPb};var SP=krc(916);IZ(917,64,nOc,xzb);_.ih=function yzb(a){return new ueb(a)};_.Hh=function zzb(a){return new ueb(a)};_.jh=function Azb(){return iQb(),eQb};var TP=krc(917);IZ(900,47,nOc,Bzb);_.ih=function Czb(a){return new Ceb}; +_.jh=function Dzb(){return iQb(),iQb(),gQb};var cQ=krc(900);IZ(918,64,nOc,Ezb);_.ih=function Fzb(a){return new ojb(a)};_.Hh=function Gzb(a){return new ojb(a)};_.jh=function Hzb(){return iQb(),iQb(),gQb};var VP=krc(918);IZ(919,64,nOc,Izb);_.ih=function Jzb(a){return new Onb(a)};_.Hh=function Kzb(a){return new Onb(a)};_.jh=function Lzb(){return iQb(),iQb(),gQb};var WP=krc(919);IZ(920,64,nOc,Mzb);_.ih=function Nzb(a){return new meb(a)};_.Hh=function Ozb(a){return new meb(a)};_.jh=function Pzb(){return iQb(), +cQb};var XP=krc(920);IZ(921,64,nOc,Qzb);_.ih=function Rzb(a){return new Rfb(a)};_.Hh=function Szb(a){return new Rfb(a)};_.jh=function Tzb(){return iQb(),cQb};var YP=krc(921);IZ(922,64,nOc,Uzb);_.ih=function Vzb(a){return new tfb(a)};_.Hh=function Wzb(a){return new tfb(a)};_.jh=function Xzb(){return iQb(),cQb};var ZP=krc(922);IZ(923,64,nOc,Yzb);_.ih=function Zzb(a){return new ahb(a)};_.Hh=function $zb(a){return new ahb(a)};_.jh=function _zb(){return iQb(),cQb};var $P=krc(923);IZ(515,64,nOc,aAb);_.ih= +function bAb(a){return new Ogb(Sgb(new Tgb(a),this.a))};_.Hh=function cAb(a){return new Ogb(Sgb(new Tgb(a),this.a))};_.jh=function dAb(){return iQb(),UPb};var _P=krc(515);IZ(516,64,nOc,eAb);_.ih=function fAb(a){return new iAb(a,this.a,this.b)};_.Hh=function gAb(a){return new iAb(a,this.a,this.b)};_.jh=function hAb(){return iQb(),iQb(),gQb};var bQ=krc(516);IZ(517,1,$Mc,iAb);_.wg=function jAb(a,b){Jyb(this.a,this.b,this.c)};var aQ=krc(517);IZ(901,64,nOc,kAb);_.ih=function lAb(a){return zub(Aub(new Bub(a), +!m9(a.G,(iQb(),$Pb))))};_.Hh=function mAb(a){return zub(Aub(new Bub(a),!m9(a.G,(iQb(),$Pb))))};_.jh=function nAb(){return iQb(),eQb};var dQ=krc(901);IZ(902,64,nOc,oAb);_.ih=function pAb(a){return Vub(Wub(new Xub(a),!m9(a.G,(iQb(),$Pb))))};_.Hh=function qAb(a){return Vub(Wub(new Xub(a),!m9(a.G,(iQb(),$Pb))))};_.jh=function rAb(){return iQb(),eQb};var eQ=krc(902);IZ(903,64,nOc,sAb);_.ih=function tAb(a){return new uvb(a)};_.Hh=function uAb(a){return new uvb(a)};_.jh=function vAb(){return iQb(),eQb}; +var fQ=krc(903);IZ(904,64,nOc,wAb);_.ih=function xAb(a){return new fvb(a)};_.Hh=function yAb(a){return new fvb(a)};_.jh=function zAb(){return iQb(),eQb};var gQ=krc(904);IZ(905,64,nOc,AAb);_.ih=function BAb(a){return new _jb(a)};_.Hh=function CAb(a){return new _jb(a)};_.jh=function DAb(){return iQb(),UPb};var hQ=krc(905);IZ(906,64,nOc,EAb);_.ih=function FAb(a){return new zeb(a)};_.Hh=function GAb(a){return new zeb(a)};_.jh=function HAb(){return iQb(),UPb};var iQ=krc(906);IZ(907,47,nOc,IAb);_.ih=function JAb(a){return new Tdb(a)}; +_.jh=function KAb(){return iQb(),eQb};var jQ=krc(907);var LAb,MAb,NAb,OAb,PAb,QAb,RAb,SAb,TAb,UAb,VAb,WAb,XAb,YAb,ZAb,$Ab,_Ab,aBb,bBb,cBb,dBb,eBb,fBb,gBb,hBb,iBb,jBb,kBb,lBb,mBb,nBb,oBb,pBb,qBb,rBb,sBb,tBb,uBb,vBb,wBb,xBb;var zBb,ABb,BBb,CBb,DBb,EBb,FBb,GBb,HBb,IBb,JBb,KBb,LBb;IZ(1001,358,{},$Bb);_.fh=function _Bb(a,b){YBb(this,a);q4(this,a,b)};var lQ=krc(1001);IZ(1002,1,_Lc,bCb);_.yd=function dCb(a){return this===a};_.Od=function cCb(a){return agc(a,1)};var kQ=krc(1002);var eCb,fCb,gCb,hCb,iCb,jCb, +kCb,lCb,mCb,nCb;IZ(680,1,$Mc,uCb);_.wg=function vCb(a,b){tCb(this,a,b)};var pCb;var mQ=krc(680);IZ(391,390,{390:1,302:1,391:1,3:1},wCb);_.Bd=function xCb(){return"Var "+this.b+" @ "+this.c};var qQ=krc(391);IZ(794,1,{45:1,22:1,317:1},LCb);_.yh=function NCb(a){};_.zh=function OCb(a){var b,c,d,e;if(!this.d&&Mob(a)==0){d=Kob(a);for(c=BCb.Vd();c.Id();){b=c.Jd();e=i$(d,b);!e&&jDc(this.e,b)}}};_.wg=function QCb(a,b){KCb(this,a,b)};_.Bg=function RCb(a,b,c){return true};_.Cg=function SCb(a,b,c){var d,e,f, +g,h,i,j,k,l,m,n,o;if(b.n==(Afc(),rec)){n=b.Wh();if(n.length==0){Xd((ppb(),c.n==Jdc&&!(c.n==Jdc&&iqb(c.g)&&c.n==Jdc&&Gqb(c.c))&&!Bqb(c)||Bqb(c)));return}j=Kob(a);k=i$(j,n);o=k?k.d:null;if(!!o&&!o.b&&(c.n==ufc||(ppb(),c.n==Jdc&&iqb(c.g)&&c.n==Jdc&&Gqb(c.c)))&&kDc(this.e,n)){MCb(this.a,n);lDc(this.e,n);d=E7b(H8b(b,(Hbc(),mbc)));g4b(d.a);d.e=true;q9b(b,k6b(d))}if(!k){ppb();if((c.n==Jdc&&!(c.n==Jdc&&iqb(c.g)&&c.n==Jdc&&Gqb(c.c))&&!Bqb(c)||c.n==bdc&&(!(c.n==bdc&&Gqb(c.c))||!iqb(c.g)))&&b==c.c);else{i=b.g; +if(!!i&&b.n==rec&&(i.n==Bdc&&b!=i.c||i.n==Wdc&&b!=(i.c?i.c.i:null)));else{g=c.n==pfc;!g&&!(this.c&&B8((!a.d&&!!a.i&&(a.d=L6(a.c,a.i)),a.d)))&&Uob(a,b,ECb,CE(xE(nW,1),uNc,2,6,[n]));if(this.d)throw gZ(new Irc("Unexpected variable "+n));else{MCb(this.a,n);lDc(this.e,n);twb(f$(j),n,b,T6(this.a))}}}return}e=(!a.d&&!!a.i&&(a.d=L6(a.c,a.i)),a.d);l=k.a;if(e==l||!e||!l)return;f=e.k;m=l.k;h=this.a.B;if(!this.d&&m!=f&&!!m&&!!f)if(f!=m&&nyc(h.c[f.c],m.c));else!j.b?m!=f&&nyc(h.c[m.c],f.c)?Uob(a,b,HCb,CE(xE(nW, +1),uNc,2,6,[f.e,m.e,n])):Uob(a,b,zCb,CE(xE(nW,1),uNc,2,6,[f.e,m.e,n])):Uob(a,b,CCb,CE(xE(nW,1),uNc,2,6,[f.e,m.e,n]))}};_.c=false;_.d=false;var yCb,zCb,ACb,BCb,CCb,DCb,ECb,FCb,GCb,HCb;var pQ=krc(794);IZ(795,1,rNc,TCb);_.Bg=function UCb(a,b,c){return b.n!=(Afc(),Xec)||!oqb(b)};_.Cg=function VCb(a,b,c){var d,e,f,g;if(b.n==(Afc(),rec)){switch(c.n.f){case 76:case 100:case 88:case 65:case 96:case 45:case 121:case 114:case 93:return;case 91:if(c.g.n==Fec)return;break;case 26:if(b==c.c){e=Kob(a);g=i$(e,b.Wh()); +if(!g){Uob(a,b,(ICb(),DCb),CE(xE(nW,1),uNc,2,6,[b.Wh()]));jDc(this.a.e,b.Wh())}}return;case 47:if(b==(c.c?c.c.i:null)&&V8b(b)&&V8b(c.c))return;break;case 29:if(Cqb(c.g))return;break;case 61:if(Dqb(c.g))return}Uob(a,b,(ICb(),ACb),CE(xE(nW,1),uNc,2,6,[b.Wh()]));d=Kob(a);f=i$(d,b.Wh());!f&&jDc(this.a.e,b.Wh())}};var nQ=krc(795);IZ(796,1,{},XCb);_.xh=function YCb(a,b,c,d){var e,f,g,h,i,j;j=Epb(c);i=i$(a,b);g=i.c;h=!g?null:Epb(g);if(j.n==(Afc(),jec)||j.n==gdc||j.n==bdc||!!h&&(h.n==jec||h.n==gdc||h.n== +bdc)){u7(this.b.a,Lmb(c,(ICb(),yCb),CE(xE(nW,1),uNc,2,6,[])));return}else if(j.n==Jdc&&!!a.b&&!!h&&(h.n==Jdc||h.n==jec||h.n==gdc||h.n==bdc)){u7(this.b.a,Lmb(c,(ICb(),yCb),CE(xE(nW,1),uNc,2,6,[])));return}if(!a.b){if(h.n==adc&&j.n==adc)return;e=PCb(this.b.a,c,i.c);ICb();if(c.g.n==ufc&&(f=H8b(c,(Hbc(),ybc)),!!f&&f.Kh())&&Dqb(c)){Ywc(this.a,j);return}e||u7(this.b.a,Lmb(c,GCb,CE(xE(nW,1),uNc,2,6,[b,i.a?i.a.i.a:"??"])))}else Esc(b,BMc)&&!(Cqb(c.g)&&c.n==rec)&&u7(this.b.a,Lmb(c,(ICb(),FCb),CE(xE(nW,1), +uNc,2,6,[])))};var oQ=krc(796);var ZCb,$Cb,_Cb;IZ(230,12,{230:1,3:1,20:1,12:1},fDb);var bDb,cDb,dDb;var rQ=lrc(230,XV,gDb);IZ(145,12,{145:1,3:1,20:1,12:1},pDb);_.a=0;var hDb,iDb,jDb,kDb,lDb,mDb,nDb;var sQ=lrc(145,XV,qDb);IZ(637,1,{637:1});var DQ=krc(637);IZ(419,637,{419:1,637:1},rDb);_.yd=function sDb(a){var b;if(a===this)return true;if(pF(a,419)){b=a;return Esc(this.a,b.a)&&Esc(this.b,b.b)}return false};_.Ad=function tDb(){var a;a=1;a*=_Mc;a^=TKc(this.a);a*=_Mc;a^=TKc(this.b);return a};_.Bd=function uDb(){return"PrefixReplacement{prefix="+ +this.a+iLc+"replacement="+this.b+"}"};var uQ=krc(419);IZ(521,1,{521:1,3:1});var vDb;var LQ=krc(521);IZ(389,521,{389:1,521:1,3:1},CDb);_.yd=function DDb(a){var b;if(a===this)return true;if(pF(a,389)){b=a;return Esc(this.b,b.b)&&Esc(this.a,b.a)&&this.c==b.c}return false};_.Ad=function EDb(){var a;a=1;a*=_Mc;a^=TKc(this.b);a*=_Mc;a^=TKc(this.a);a*=_Mc;a^=NKc(this.c);return a};_.Bd=function FDb(){return"Require{symbol="+this.b+iLc+"rawText="+this.a+iLc+"type="+this.c+"}"};var wQ=krc(389);IZ(1095,1,{}); +var JQ=krc(1095);IZ(205,1095,{},KDb);var vQ=krc(205);IZ(520,239,{431:1,239:1,520:1,3:1});var LDb;var cR=krc(520);IZ(387,520,{387:1,431:1,239:1,520:1,3:1},ODb);_.yd=function PDb(a){var b;if(a===this)return true;if(pF(a,387)){b=a;return Esc(this.d,b.d)&&Esc(this.e,b.e)&&this.f.yd(b.f)&&this.g.yd(b.g)&&this.i.yd(b.i)&&this.c.yd(b.c)&&this.a==b.a&&this.b==b.b}return false};_.lh=function QDb(){return this.c};_.ug=function RDb(){return this.d};_.mh=function SDb(){return this.f};_.oh=function TDb(){return this.g}; +_.ph=function UDb(){return this.i};_.Ad=function VDb(){var a;a=1;a*=_Mc;a^=TKc(this.d);a*=_Mc;a^=TKc(this.e);a*=_Mc;a^=this.f.Ad();a*=_Mc;a^=this.g.Ad();a*=_Mc;a^=this.i.Ad();a*=_Mc;a^=this.c.Ad();a*=_Mc;a^=this.a?1231:1237;a*=_Mc;a^=this.b?1231:1237;return a};_.Bd=function WDb(){return"SimpleDependencyInfo{name="+this.d+iLc+"pathRelativeToClosureBase="+this.e+iLc+"provides="+this.f+iLc+"requires="+this.g+iLc+"typeRequires="+this.i+iLc+"loadFlags="+this.c+iLc+"hasExternsAnnotation="+this.a+iLc+"hasNoCompileAnnotation="+ +this.b+"}"};_.a=false;_.b=false;var yQ=krc(387);IZ(1094,1,{});var bR=krc(1094);IZ(388,1094,{},fEb);var xQ=krc(388);IZ(355,1,{});_.Mh=function lEb(a,b){return iEb(this,a,b)};var ZQ=krc(355);IZ(774,355,{},oEb);_.Lh=function pEb(a,b,c,d,e){var f;ZEb();if(!Esc(b.substr(0,"/".length),"/")&&!(Esc(b.substr(0,rOc.length),rOc)||Esc(b.substr(0,nRc.length),nRc))){this.d.xg((E2(),D2),new Gmb(c,null,d,e,XEb,null,CE(xE(nW,1),uNc,2,6,[b,ge((NFb(),JFb))])));return null}f=hEb(this,a,b);f==null&&this.d.xg((E2(),D2), +new Gmb(c,null,d,e,YEb,null,CE(xE(nW,1),uNc,2,6,[b])));return f};var mEb;var AQ=krc(774);IZ(775,1,{},qEb);_.Nh=function rEb(a,b,c,d){return new oEb(a,b,c,d)};var zQ=krc(775);IZ(851,355,{},uEb);_.Lh=function vEb(a,b,c,d,e){var f,g,h,i,j;j=b;i=false;for(h=this.b.Vd();h.Id();){g=h.Jd();if(Ssc(b,g.a)){j=g.b+(""+Usc(b,g.a.length));i=true;break}}if(!i&&(ZEb(),!Esc(j.substr(0,1),"/")&&!(Esc(j.substr(0,2),rOc)||Esc(j.substr(0,3),nRc)))){this.d.xg((E2(),D2),new Gmb(c,null,d,e,sEb,null,CE(xE(nW,1),uNc,2,6, +[j,this.a])));return null}f=hEb(this,a,j);j==null&&this.d.xg((E2(),D2),new Gmb(c,null,d,e,(ZEb(),YEb),null,CE(xE(nW,1),uNc,2,6,[b])));return f};_.Mh=function wEb(a,b){var c,d,e;ZEb();if(Esc(b.substr(0,rOc.length),rOc)||Esc(b.substr(0,nRc.length),nRc))return iEb(this,a,b);e=b;for(d=this.b.Vd();d.Id();){c=d.Jd();if(Ssc(b,c.a)){e=c.b+(""+Usc(b,c.a.length));break}}return dFb(e,this.f)};var sEb;var GQ=krc(851);IZ(854,1,BLc,xEb);_.Od=function yEb(a){return a.a};var BQ=krc(854);IZ(675,1,{},zEb);_.Nh=function AEb(a, +b,c,d){return new uEb(a,b,c,d,this.a)};var CQ=krc(675);IZ(852,1,BLc,BEb);_.Od=function CEb(a){return tEb(),new rDb(a.Ve(),a.We())};var EQ=krc(852);IZ(853,1,{},DEb);_.Oh=function EEb(a){return tEb(),a.a.length};var FQ=krc(853);IZ(441,1,BLc,FEb);_.Od=function GEb(a){return a.b};var IQ=krc(441);IZ(172,12,{172:1,3:1,20:1,12:1},NEb);var HEb,IEb,JEb,KEb,LEb;var KQ=lrc(172,XV,OEb);IZ(870,1,{},VEb);var MQ=krc(870);IZ(320,1,{},aFb,bFb);var WEb,XEb,YEb;var YQ=krc(320);IZ(693,1,_Lc,fFb);_.yd=function hFb(a){return this=== +a};_.Od=function gFb(a){return a.ug()};var NQ=krc(693);IZ(694,1,{},iFb);_.Oh=function jFb(a){return a.length};var OQ=krc(694);IZ(695,1,EQc,kFb);_.yd=function mFb(a){return this===a};_.nf=function nFb(){return new SBc(this)};_.lf=function lFb(a,b){return Asc(a,b)};var PQ=krc(695);IZ(323,1,{},qFb);_.Bd=function rFb(){return this.a};var QQ=krc(323);IZ(451,1,{},sFb);_.xg=function tFb(a,b){};var RQ=krc(451);IZ(190,12,oRc);var uFb,vFb;var UQ=lrc(190,XV,yFb);IZ(690,190,oRc,zFb);_.Ph=function AFb(a){var b; +return RFb(),b=Osc(Osc(Osc(Osc(Osc(Nsc(Nsc(a,58,45),92,47)," ",cNc),"[",dNc),"]",eNc),"<",fNc),">",gNc),SFb(b)};var SQ=lrc(690,UQ,null);IZ(691,190,oRc,BFb);_.Ph=function CFb(a){return SFb(a)};var TQ=lrc(691,UQ,null);IZ(264,12,pRc);var DFb;var WQ=lrc(264,XV,FFb);IZ(692,264,pRc,HFb);_.Od=function IFb(a){return GFb(a)};var VQ=lrc(692,WQ,null);IZ(191,12,{191:1,3:1,20:1,12:1},OFb);var JFb,KFb,LFb,MFb;var XQ=lrc(191,XV,PFb);var QFb;IZ(493,355,{},bGb);_.Lh=function eGb(a,b,c,d,e){return ZFb(this,a,b,c,d, +e)};var VFb,WFb;var aR=krc(493);IZ(673,1,{},fGb);_.Nh=function gGb(a,b,c,d){return new bGb(a,b,this.a,c,d)};var $Q=krc(673);IZ(849,1,EQc,hGb);_.yd=function jGb(a){return this===a};_.nf=function kGb(){return new SBc(this)};_.lf=function iGb(a,b){return dGb(a,b)};var _Q=krc(849);IZ(219,38,{219:1,3:1,38:1,36:1},lGb,mGb);var dR=krc(219);IZ(850,493,{},nGb);_.Lh=function oGb(a,b,c,d,e){var f;f=this.a.Ie(b);if(f==null)return ZFb(this,a,b,c,d,e);return f};var fR=krc(850);IZ(674,1,{},pGb);_.Nh=function qGb(a, +b,c,d){var e,f;f=new fDc;for(null.Ij().Ij();null.Ij();){null.Ij();e=dFb(d.Ph(null.Ij()),b);ZEb();!Esc(e.substr(0,"/".length),"/")&&!(Esc(e.substr(0,rOc.length),rOc)||Esc(e.substr(0,nRc.length),nRc))&&(e="/"+e);_tc(f,null.Ij(),e)}return new nGb(a,b,f,c,d)};var eR=krc(674);IZ(1046,1,{});var rGb;var iR=krc(1046);IZ(649,651,{},wGb);var gR=krc(649);IZ(650,436,{},xGb);_.yg=function yGb(){this.d+this.e+this.f>0&&uGb(this.d+this.e+" error(s), "+this.f+" warning(s)")};_.zg=function zGb(a,b){uGb(Fmb(b,a,this.a))}; +var hR=krc(650);IZ(258,857,{},CGb);var jR=krc(258);var DGb;var FGb;var HGb;var JGb;var LGb,MGb,NGb,OGb;var QGb;var SGb,TGb;var VGb,WGb;var YGb,ZGb,$Gb,_Gb,aHb,bHb,cHb,dHb,eHb,fHb,gHb,hHb,iHb;var kHb;var mHb;var oHb,pHb,qHb;var sHb,tHb;var vHb,wHb;var yHb;var AHb,BHb,CHb,DHb;var FHb;var HHb;var JHb;IZ(31,12,{31:1,3:1,20:1,12:1},UIb);var LHb,MHb,NHb,OHb,PHb,QHb,RHb,SHb,THb,UHb,VHb,WHb,XHb,YHb,ZHb,$Hb,_Hb,aIb,bIb,cIb,dIb,eIb,fIb,gIb,hIb,iIb,jIb,kIb,lIb,mIb,nIb,oIb,pIb,qIb,rIb,sIb,tIb,uIb,vIb,wIb,xIb, +yIb,zIb,AIb,BIb,CIb,DIb,EIb,FIb,GIb,HIb,IIb,JIb,KIb,LIb,MIb,NIb,OIb,PIb,QIb,RIb,SIb;var kR=lrc(31,XV,VIb);IZ(601,1,{601:1});var sR=krc(601);IZ(413,601,{413:1,601:1},XIb);_.yd=function YIb(a){var b;if(a===this)return true;if(pF(a,413)){b=a;return this.d==b.d&&this.g==b.g&&this.c==b.c&&this.f==b.f&&Oo(this.a,b.a)&&qz(this.i,b.i)&&qz(this.b,b.b)&&this.e==b.e}return false};_.Ad=function ZIb(){var a;a=1;a*=_Mc;a^=NKc(this.d);a*=_Mc;a^=NKc(this.g);a*=_Mc;a^=NKc(this.c);a*=_Mc;a^=NKc(this.f);a*=_Mc;a^=Db(this.a.d.d); +a*=_Mc;a^=this.i.Ad();a*=_Mc;a^=this.b.Ad();a*=_Mc;a^=this.e?1231:1237;return a};_.Bd=function $Ib(){return"Config{languageMode="+this.d+iLc+"strictMode="+this.g+iLc+"jsDocParsingMode="+this.c+iLc+"runMode="+this.f+iLc+"annotations="+this.a+iLc+"suppressionNames="+this.i+iLc+"closurePrimitiveNames="+this.b+iLc+"parseInlineSourceMaps="+this.e+"}"};_.e=false;var mR=krc(413);IZ(1102,1,{});var nR=krc(1102);IZ(414,1102,{},iJb);var lR=krc(414);IZ(234,12,{234:1,3:1,20:1,12:1},nJb);var jJb,kJb,lJb;var oR= +lrc(234,XV,oJb);IZ(106,12,{106:1,3:1,20:1,12:1},AJb);var pJb,qJb,rJb,sJb,tJb,uJb,vJb,wJb,xJb,yJb;var pR=lrc(106,XV,CJb);IZ(271,12,{271:1,3:1,20:1,12:1},GJb);var DJb,EJb;var qR=lrc(271,XV,HJb);IZ(270,12,{270:1,3:1,20:1,12:1},LJb);var IJb,JJb;var rR=lrc(270,XV,MJb);IZ(897,1,{},IKb);_.c=false;_.g=null;_.i=false;var NJb,OJb,PJb,QJb;var uR=krc(897);IZ(898,1,{},tMb);var tR=krc(898);IZ(406,1,{},BNb);_.e=null;_.g=false;_.o=null;var uMb,vMb,wMb;var yR=krc(406);IZ(595,1,{595:1},ENb);_.a=0;_.b=0;var vR=krc(595); +IZ(407,1,{},FNb);var wR=krc(407);IZ(252,12,{252:1,3:1,20:1,12:1},KNb);var GNb,HNb,INb;var xR=lrc(252,XV,LNb);IZ(67,12,{67:1,3:1,20:1,12:1},gOb);var MNb,NNb,ONb,PNb,QNb,RNb,SNb,TNb,UNb,VNb,WNb,XNb,YNb,ZNb,$Nb,_Nb,aOb,bOb,cOb,dOb,eOb;var AR=lrc(67,XV,hOb);IZ(254,1,{},rOb,sOb);_.a=-1;_.b=0;_.c=0;_.d=0;_.e=-1;_.f=0;_.g=0;_.i=0;_.j=0;_.n="";_.p=0;_.r=0;var zR=krc(254);var vOb=null,wOb=null,xOb=null;IZ(1086,1,{});_.d=false;var TT=krc(1086);IZ(867,1086,{},FOb);_.Qh=function GOb(a,b){if(this.b||!this.a){this.a= +true;this.c.Ah(b,a.d.c,a.b+1,a.a)}};_.Rh=function HOb(a,b){this.c.Bh(b,a.d.c,a.b+1,a.a)};_.a=false;_.b=false;var BR=krc(867);IZ(868,1,{},IOb);var CR=krc(868);IZ(1030,1,{},oPb);_.c=0;_.d=0;var ER=krc(1030);IZ(59,12,{59:1,3:1,20:1,12:1},OPb);_.a=0;_.b=0;_.c=0;var pPb,qPb,rPb,sPb,tPb,uPb,vPb,wPb,xPb,yPb,zPb,APb,BPb,CPb,DPb,EPb,FPb,GPb,HPb,IPb,JPb,KPb,LPb,MPb;var DR=lrc(59,XV,QPb);var RPb;IZ(124,1,{124:1,3:1},uQb);_.yd=function xQb(a){return pF(a,124)&&qz(a.a,this.a)};_.Ad=function yQb(){return this.a.Ad()}; +_.Bd=function zQb(){return this.a.Bd()};var TPb,UPb,VPb,WPb,XPb,YPb,ZPb,$Pb,_Pb,aQb,bQb,cQb,dQb,eQb,fQb,gQb,hQb;var GR=krc(124);IZ(24,12,{24:1,3:1,20:1,12:1},JRb);_.Bd=function KRb(){return this.a};_.b=0;var CQb,DQb,EQb,FQb,GQb,HQb,IQb,JQb,KQb,LQb,MQb,NQb,OQb,PQb,QQb,RQb,SQb,TQb,UQb,VQb,WQb,XQb,YQb,ZQb,$Qb,_Qb,aRb,bRb,cRb,dRb,eRb,fRb,gRb,hRb,iRb,jRb,kRb,lRb,mRb,nRb,oRb,pRb,qRb,rRb,sRb,tRb,uRb,vRb,wRb,xRb,yRb,zRb,ARb,BRb,CRb,DRb,ERb,FRb,GRb,HRb;var FR=lrc(24,XV,LRb);IZ(152,1,{152:1},NRb);_.Bd=function ORb(){return BZb(this.e)}; +var YR=krc(152);IZ(415,152,{415:1,152:1},PRb);_.Bd=function QRb(){return this.a};var HR=krc(415);IZ(37,12,{37:1,3:1,20:1,12:1},PSb);_.Bd=function WSb(){return this.c};_.a=false;var RRb,SRb,TRb,URb,VRb,WRb,XRb,YRb,ZRb,$Rb,_Rb,aSb,bSb,cSb,dSb,eSb,fSb,gSb,hSb,iSb,jSb,kSb,lSb,mSb,nSb,oSb,pSb,qSb,rSb,sSb,tSb,uSb,vSb,wSb,xSb,ySb,zSb,ASb,BSb,CSb,DSb,ESb,FSb,GSb,HSb,ISb,JSb,KSb,LSb,MSb,NSb;var IR=lrc(37,XV,XSb);IZ(936,1,{},_Sb);var KR=krc(936);IZ(937,1,{},cTb);var JR=krc(937);IZ(99,152,{99:1,152:1},dTb); +_.Bd=function eTb(){return this.c};var LR=krc(99);IZ(298,1,{},YVb,ZVb);var RR=krc(298);IZ(895,1,{},_Vb);var MR=krc(895);IZ(894,1,{},aWb);_.a=false;_.b=false;_.c=false;_.d=false;_.e=false;var OR=krc(894);IZ(149,12,{149:1,3:1,20:1,12:1},jWb);var bWb,cWb,dWb,eWb,fWb,gWb,hWb;var NR=lrc(149,XV,kWb);IZ(202,12,{202:1,3:1,20:1,12:1},qWb);_.a=false;_.b=false;var lWb,mWb,nWb,oWb;var PR=lrc(202,XV,rWb);IZ(203,1,{},sWb);_.a=null;_.b=false;_.c=false;var QR=krc(203);IZ(896,1,{},fXb);_.c=0;_.f=0;_.g=false;_.j=0; +var TR=krc(896);IZ(178,1,{},rXb);var SR=krc(178);IZ(893,1,{},sXb);var UR=krc(893);IZ(424,99,{99:1,424:1,152:1},tXb);_.a=false;var VR=krc(424);IZ(253,99,{99:1,253:1,152:1},uXb);_.Bd=function vXb(){return this.c};var WR=krc(253);IZ(14,12,{14:1,3:1,20:1,12:1},CZb,DZb);_.Bd=function EZb(){return BZb(this)};var wXb,xXb,yXb,zXb,AXb,BXb,CXb,DXb,EXb,FXb,GXb,HXb,IXb,JXb,KXb,LXb,MXb,NXb,OXb,PXb,QXb,RXb,SXb,TXb,UXb,VXb,WXb,XXb,YXb,ZXb,$Xb,_Xb,aYb,bYb,cYb,dYb,eYb,fYb,gYb,hYb,iYb,jYb,kYb,lYb,mYb,nYb,oYb,pYb,qYb, +rYb,sYb,tYb,uYb,vYb,wYb,xYb,yYb,zYb,AYb,BYb,CYb,DYb,EYb,FYb,GYb,HYb,IYb,JYb,KYb,LYb,MYb,NYb,OYb,PYb,QYb,RYb,SYb,TYb,UYb,VYb,WYb,XYb,YYb,ZYb,$Yb,_Yb,aZb,bZb,cZb,dZb,eZb,fZb,gZb,hZb,iZb,jZb,kZb,lZb,mZb,nZb,oZb,pZb,qZb,rZb,sZb,tZb,uZb,vZb,wZb,xZb,yZb,zZb;var XR=lrc(14,XV,FZb);IZ(8,1,{8:1});_.Bd=function JZb(){return this.p+"@"+this.o};var qT=krc(8);IZ(591,8,{591:1,8:1},KZb);var ZR=krc(591);IZ(971,8,{8:1},LZb);var $R=krc(971);IZ(525,8,{525:1,8:1},MZb);var _R=krc(525);IZ(571,8,{571:1,8:1},NZb);var aS= +krc(571);IZ(582,8,{582:1,8:1},OZb);var bS=krc(582);IZ(573,8,{573:1,8:1},PZb);var cS=krc(573);IZ(564,8,{564:1,8:1},QZb);var dS=krc(564);IZ(98,8,{98:1,8:1},RZb);var eS=krc(98);IZ(396,8,{396:1,8:1},SZb);var fS=krc(396);IZ(527,8,{527:1,8:1},TZb);var gS=krc(527);IZ(528,8,{528:1,8:1},UZb);var hS=krc(528);IZ(594,8,{594:1,8:1},VZb);_.c=false;var iS=krc(594);IZ(529,8,{529:1,8:1},WZb);var jS=krc(529);IZ(531,8,{531:1,8:1},XZb);var kS=krc(531);IZ(560,8,{560:1,8:1},YZb);var lS=krc(560);IZ(403,8,{403:1,8:1},ZZb); +var mS=krc(403);IZ(519,1,{519:1},$Zb);var oS=krc(519);IZ(179,12,{179:1,3:1,20:1,12:1},f$b);var _Zb,a$b,b$b,c$b,d$b;var nS=lrc(179,XV,g$b);IZ(575,8,{575:1,8:1},h$b);var pS=krc(575);IZ(576,8,{576:1,8:1},i$b);var qS=krc(576);IZ(574,8,{574:1,8:1},j$b);var rS=krc(574);IZ(400,8,{400:1,8:1},k$b);var sS=krc(400);IZ(545,8,{545:1,8:1},l$b);_.b=false;var tS=krc(545);IZ(546,8,{546:1,8:1},m$b);_.c=false;var uS=krc(546);IZ(207,8,{207:1,8:1},n$b);var vS=krc(207);IZ(547,8,{547:1,8:1},o$b);_.b=false;var wS=krc(547); +IZ(540,8,{540:1,8:1},p$b);var xS=krc(540);IZ(532,8,{532:1,8:1},q$b);var yS=krc(532);IZ(535,8,{535:1,8:1},r$b);var zS=krc(535);IZ(530,8,{530:1,8:1},s$b);var AS=krc(530);IZ(251,8,{251:1,8:1},t$b);var BS=krc(251);IZ(533,8,{533:1,8:1},u$b);var CS=krc(533);IZ(570,8,{570:1,8:1},v$b);var DS=krc(570);IZ(180,8,{180:1,8:1},w$b);var ES=krc(180);IZ(589,8,{589:1,8:1},x$b);var FS=krc(589);IZ(567,8,{567:1,8:1},y$b);_.d=false;_.e=false;var GS=krc(567);IZ(568,8,{568:1,8:1},z$b);var HS=krc(568);IZ(534,8,{534:1,8:1}, +A$b);var IS=krc(534);IZ(557,8,{557:1,8:1},B$b);var JS=krc(557);IZ(566,8,{566:1,8:1},C$b);var KS=krc(566);IZ(538,8,{538:1,8:1},D$b);var LS=krc(538);IZ(565,8,{565:1,8:1},E$b);var MS=krc(565);IZ(537,8,{537:1,8:1},F$b);var NS=krc(537);IZ(181,8,{181:1,8:1},G$b);var OS=krc(181);IZ(539,8,{539:1,8:1},H$b);_.e=false;_.f=false;_.g=false;_.i=false;var RS=krc(539);IZ(92,1,{},T$b);_.a=null;_.b=null;_.c=null;_.d=null;_.e=false;_.f=false;_.g=false;_.i=false;_.n=null;_.o=null;var PS=krc(92);IZ(206,12,{206:1,3:1, +20:1,12:1},Z$b);var U$b,V$b,W$b,X$b;var QS=lrc(206,XV,$$b);IZ(585,8,{585:1,8:1},_$b);var SS=krc(585);IZ(587,8,{587:1,8:1},a_b);var TS=krc(587);IZ(558,8,{558:1,8:1},b_b);_.b=false;var US=krc(558);IZ(304,8,{304:1,8:1},c_b);var VS=krc(304);IZ(541,8,{541:1,8:1},d_b);var WS=krc(541);IZ(404,8,{404:1,8:1},e_b);var XS=krc(404);IZ(569,8,{569:1,8:1},f_b);var YS=krc(569);IZ(593,8,{593:1,8:1},g_b);var ZS=krc(593);IZ(588,8,{588:1,8:1},h_b);var $S=krc(588);IZ(542,8,{542:1,8:1},i_b);var _S=krc(542);IZ(402,8,{402:1, +8:1},j_b);var aT=krc(402);IZ(398,8,{398:1,8:1},k_b);var bT=krc(398);IZ(397,8,{397:1,8:1},l_b);var cT=krc(397);IZ(305,8,{305:1,8:1},m_b);_.c=false;_.d=false;var dT=krc(305);IZ(209,8,{209:1,8:1},n_b);var eT=krc(209);IZ(592,8,{592:1,8:1},o_b);var fT=krc(592);IZ(972,8,{8:1},p_b);var gT=krc(972);IZ(399,8,{399:1,8:1},q_b);var hT=krc(399);IZ(562,8,{562:1,8:1},r_b);var iT=krc(562);IZ(208,8,{208:1,8:1},s_b);var jT=krc(208);IZ(544,8,{544:1,8:1},t_b);var kT=krc(544);IZ(572,8,{572:1,8:1},u_b);var lT=krc(572); +IZ(580,8,{580:1,8:1},v_b);var mT=krc(580);IZ(581,8,{581:1,8:1},w_b);var nT=krc(581);IZ(543,8,{543:1,8:1},x_b);var oT=krc(543);IZ(19,12,{19:1,3:1,20:1,12:1},o1b);var y_b,z_b,A_b,B_b,C_b,D_b,E_b,F_b,G_b,H_b,I_b,J_b,K_b,L_b,M_b,N_b,O_b,P_b,Q_b,R_b,S_b,T_b,U_b,V_b,W_b,X_b,Y_b,Z_b,$_b,__b,a0b,b0b,c0b,d0b,e0b,f0b,g0b,h0b,i0b,j0b,k0b,l0b,m0b,n0b,o0b,p0b,q0b,r0b,s0b,t0b,u0b,v0b,w0b,x0b,y0b,z0b,A0b,B0b,C0b,D0b,E0b,F0b,G0b,H0b,I0b,J0b,K0b,L0b,M0b,N0b,O0b,P0b,Q0b,R0b,S0b,T0b,U0b,V0b,W0b,X0b,Y0b,Z0b,$0b,_0b, +a1b,b1b,c1b,d1b,e1b,f1b,g1b,h1b,i1b,j1b,k1b,l1b,m1b;var pT=lrc(19,XV,p1b);IZ(512,8,{8:1,512:1},q1b);var rT=krc(512);IZ(250,8,{8:1,250:1},r1b);var sT=krc(250);IZ(583,8,{8:1,583:1},s1b);var tT=krc(583);IZ(577,8,{8:1,577:1},t1b);var uT=krc(577);IZ(548,8,{8:1,548:1},u1b);var vT=krc(548);IZ(559,8,{8:1,559:1},v1b);_.b=false;var wT=krc(559);IZ(578,8,{8:1,578:1},w1b);var xT=krc(578);IZ(561,8,{8:1,561:1},x1b);var yT=krc(561);IZ(549,8,{8:1,549:1},y1b);var zT=krc(549);IZ(393,8,{8:1,393:1},z1b);var AT=krc(393); +IZ(394,8,{8:1,394:1},A1b);var BT=krc(394);IZ(395,8,{8:1,395:1},B1b);var CT=krc(395);IZ(536,8,{8:1,536:1},C1b);var DT=krc(536);IZ(550,8,{8:1,550:1},D1b);var ET=krc(550);IZ(551,8,{8:1,551:1},E1b);var FT=krc(551);IZ(590,8,{8:1,590:1},F1b);var GT=krc(590);IZ(405,8,{8:1,405:1},G1b);var HT=krc(405);IZ(586,8,{8:1,586:1},H1b);var IT=krc(586);IZ(579,8,{8:1,579:1},I1b);var JT=krc(579);IZ(526,8,{8:1,526:1},J1b);var KT=krc(526);IZ(584,8,{8:1,584:1},K1b);var LT=krc(584);IZ(401,8,{8:1,401:1},L1b);_.c=0;var MT= +krc(401);IZ(553,8,{8:1,553:1},M1b);var NT=krc(553);IZ(554,8,{8:1,554:1},N1b);var OT=krc(554);IZ(552,8,{8:1,552:1},O1b);var PT=krc(552);IZ(555,8,{8:1,555:1},P1b);var QT=krc(555);IZ(556,8,{8:1,556:1},Q1b);var RT=krc(556);IZ(563,8,{8:1,563:1},R1b);_.b=false;var ST=krc(563);IZ(1032,1086,{},S1b);_.Qh=function T1b(a,b){throw gZ(new V1b);};_.Rh=function U1b(a,b){};var VT=krc(1032);IZ(425,25,{425:1,3:1,38:1,25:1,36:1},V1b);var UT=krc(425);IZ(385,1,{},X1b);_.Bd=function Y1b(){return x2b("%s(%d, %d)",CE(xE(iW, +1),mLc,1,5,[W1b(this),Yrc(this.b+1),Yrc(this.a+1)]))};_.a=0;_.b=0;_.c=0;var WT=krc(385);IZ(13,1,{},Z1b);_.Bd=function $1b(){return x2b("<%s - %s>",CE(xE(iW,1),mLc,1,5,[this.b,this.a]))};var XT=krc(13);IZ(865,25,nMc,_1b);var YT=krc(865);IZ(866,25,nMc,a2b);var ZT=krc(866);IZ(373,25,nMc,b2b);var $T=krc(373);IZ(863,25,nMc,c2b);var _T=krc(863);IZ(864,25,nMc,d2b);var aU=krc(864);IZ(369,25,nMc,e2b);var bU=krc(369);IZ(862,25,nMc,f2b);var cU=krc(862);IZ(777,1,{},w2b);var fU=krc(777);IZ(779,1,{},H2b);_.b=0; +_.c=0;_.d=0;var dU=krc(779);IZ(778,1,{},P2b);_.a=-1;_.b=dLc;_.c=0;_.d=false;_.e=false;_.f=false;_.g=false;_.i=false;_.j=false;_.k=false;_.n=-1;_.p=-1;var eU=krc(778);IZ(370,25,nMc,Q2b);var gU=krc(370);IZ(892,1,{},S2b);var hU=krc(892);IZ(1162,1,{});var W2b;IZ(759,1,{},b3b);_.Ah=function c3b(a,b,c,d){};_.Bh=function d3b(a,b,c,d){};var iU=krc(759);IZ(259,1,{259:1,3:1},_3b);_.yd=function a4b(a){if(this===a)return true;if(a==null)return false;if(jU!=Bb(a))return false;return Esc(this.a,a.a)};_.Ad=function b4b(){return TKc(this.a)}; +_.Bd=function c4b(){return"InputId: "+this.a};var jU=krc(259);IZ(365,1,{365:1,3:1},A5b,B5b);_.Bd=function E5b(){return"JSDocInfo"};_.a=0;_.c=false;_.e=false;_.f=0;var tU=krc(365);IZ(841,1,wNc,F5b);var kU=krc(841);IZ(58,1,wNc,L5b);_.Bd=function M5b(){var a;return Cd(Bd(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(Ad(new Dd((drc(lU),lU.n)),"bitfield",this.p==0?null:(a=this.p>>>0,a.toString(16))),"baseType",this.a),"extendedInterfaces",this.f),"implementedInterfaces",this.g),"parameters",this.o), +"thrownTypes",this.s),"templateTypeNames",this.r),"disposedParameters",this.e),"typeTransformations",this.t),"description",this.d),"meaning",this.k),SOc,this.c),"license",this.j),"suppressions",this.q),"modifies",this.n),"lendsName",this.i),"closurePrimitiveId",this.b)))};_.p=0;var lU=krc(58);IZ(492,1,{492:1},Q5b);var mU=krc(492);IZ(366,1,{});_.b=0;_.c=0;_.d=null;_.e=0;_.f=0;var IU=krc(366);IZ(842,366,{},U5b);var nU=krc(842);IZ(490,366,{},W5b);var oU=krc(490);IZ(491,490,{},Y5b);var pU=krc(491);IZ(843, +366,{},Z5b);_.a=false;var qU=krc(843);IZ(177,12,{177:1,3:1,20:1,12:1},f6b);var _5b,a6b,b6b,c6b,d6b;var rU=lrc(177,XV,g6b);IZ(140,1,{},B7b,C7b);_.d=false;_.e=false;var sU=krc(140);var F7b;IZ(79,1,{79:1,3:1},H7b);_.yd=function I7b(a){return pF(a,79)&&a.a.Xh(this.a,false,true,false,false)};_.Ad=function J7b(){return Db(this.a)};_.Bd=function K7b(){return"type: "+E9b(this.a)};var uU=krc(79);IZ(7,1,{7:1,3:1},J9b,K9b,L9b,M9b,N9b,O9b,P9b);_.Sh=function R9b(a){return y8b(this,new J9b(this.n),a)};_.Th=function U9b(){if(this.n== +(Afc(),jfc))throw gZ(new Irc(rTc));else throw gZ(new ztc(this+sTc));};_.Uh=function V9b(){if(this.n==(Afc(),Cec))throw gZ(new Irc(tTc));else throw gZ(new ztc(this+" is not a number node"));};_.Vh=function W9b(){if(this.n==(Afc(),jfc))throw gZ(new Irc(rTc));else throw gZ(new ztc(this+sTc));};_.Wh=function X9b(){if(this.n==(Afc(),bfc))throw gZ(new Irc(uTc));else throw gZ(new ztc(this+vTc));};_.Xh=function Y9b(a,b,c,d,e){return U8b(this,a,b,c,d,e)};_.Yh=function Z9b(){return false};_.Zh=function _9b(a){if(this.n== +(Afc(),Cec))throw gZ(new Irc(tTc));else throw gZ(new ztc(this+vTc));};_.$h=function aac(){throw gZ(new Irc(this+" is not a StringNode"));};_._h=function bac(a){if(this.n==(Afc(),bfc)||this.n==rec)throw gZ(new Irc(uTc));else throw gZ(new ztc(this+vTc));};_.Bd=function cac(){return D9b(this,true,true,true)};_.e=0;_.k=0;var L7b,M7b,N7b,O7b,P7b,Q7b,R7b,S7b,T7b,U7b,V7b,W7b,X7b,Y7b,Z7b,$7b,_7b,a8b,b8b,c8b,d8b,e8b,f8b,g8b,h8b,i8b,j8b,k8b,l8b,m8b;var HU=krc(7);IZ(340,1,uLc,eac);_.Zd=function gac(){return new uFc(new iac(this))}; +_.Vd=function fac(){return new iac(this)};var wU=krc(340);IZ(238,1,cLc,iac);_.Hd=function jac(a){lEc(this,a)};_.Jd=function lac(){return hac(this)};_.Id=function kac(){return!!this.a.a};_.Kd=function mac(){throw gZ(new ytc);};var vU=krc(238);IZ(463,1,wNc);_.c=0;var AU=krc(463);IZ(339,463,wNc,oac);_.ai=function pac(a){return new oac(this.c,this.a,a)};_.bi=function qac(){return this.a};_.ci=function rac(){throw gZ(new ytc);};_.Bd=function sac(){return""+this.a};_.a=0;var xU=krc(339);IZ(96,7,{7:1,96:1, +3:1},tac);_.Sh=function uac(a){return y8b(this,new tac(this.a),a)};_.Uh=function vac(){return this.a};_.Xh=function wac(a,b,c,d,e){var f,g,h;f=U8b(this,a,b,c,d,e);if(f){h=this.a;g=a.a;if(h==g)return h!=0||1/h==1/g}return false};_.Zh=function xac(a){this.a=a};_.a=0;var yU=krc(96);IZ(464,463,wNc,yac);_.ai=function zac(a){return new yac(this.c,this.a,a)};_.bi=function Aac(){throw gZ(new ytc);};_.ci=function Bac(){return this.a};_.Bd=function Cac(){return etc(this.a)};var zU=krc(464);IZ(33,12,{33:1,3:1, +20:1,12:1},Ibc);var Dac,Eac,Fac,Gac,Hac,Iac,Jac,Kac,Lac,Mac,Nac,Oac,Pac,Qac,Rac,Sac,Tac,Uac,Vac,Wac,Xac,Yac,Zac,$ac,_ac,abc,bbc,cbc,dbc,ebc,fbc,gbc,hbc,ibc,jbc,kbc,lbc,mbc,nbc,obc,pbc,qbc,rbc,sbc,tbc,ubc,vbc,wbc,xbc,ybc,zbc,Abc,Bbc,Cbc,Dbc,Ebc,Fbc,Gbc;var BU=lrc(33,XV,Jbc);IZ(55,1,uLc,Kbc);_.Zd=function Mbc(){return new uFc(new Obc(this.a))};_.Vd=function Lbc(){return new Obc(this.a)};var CU=krc(55);IZ(465,1,cLc,Obc);_.Hd=function Pbc(a){lEc(this,a)};_.Jd=function Rbc(){return Nbc(this)};_.Id=function Qbc(){return!!this.a}; +_.Kd=function Sbc(){throw gZ(new ytc);};var DU=krc(465);IZ(9,7,{7:1,9:1,3:1},Vbc,Wbc,Xbc);_.Sh=function Ybc(a){return Tbc(this,a)};_.Wh=function Zbc(){return this.a};_.Xh=function $bc(a,b,c,d,e){return U8b(this,a,b,c,d,e)&&this.a==a.a};_.Yh=function _bc(){return E8b(this,(Hbc(),ubc))!=0};_.$h=function acc(){c9b(this,(Hbc(),ubc),1)};_._h=function bcc(a){Ubc(this,a)};var EU=krc(9);IZ(338,7,{7:1,338:1,3:1},dcc,ecc);_.Sh=function fcc(a){var b;return b=new dcc,b.b=this.b,b.a=this.a,y8b(this,b,a)};_.Th= +function gcc(){return this.a};_.Vh=function hcc(){return this.b};_.Xh=function icc(a,b,c,d,e){return U8b(this,a,b,c,d,e)&&this.b==a.b&&this.a==a.a};var FU=krc(338);IZ(86,7,{7:1,86:1,3:1},jcc,kcc,lcc);_.Sh=function mcc(a){return y8b(this,new lcc(this.n,this.a),a)};_.Wh=function ncc(){return this.a};var GU=krc(86);IZ(228,12,{228:1,3:1,20:1,12:1},tcc);var pcc,qcc,rcc;var JU=lrc(228,XV,ucc);IZ(10,12,{10:1,3:1,20:1,12:1},Bfc);var vcc,wcc,xcc,ycc,zcc,Acc,Bcc,Ccc,Dcc,Ecc,Fcc,Gcc,Hcc,Icc,Jcc,Kcc,Lcc,Mcc, +Ncc,Occ,Pcc,Qcc,Rcc,Scc,Tcc,Ucc,Vcc,Wcc,Xcc,Ycc,Zcc,$cc,_cc,adc,bdc,cdc,ddc,edc,fdc,gdc,hdc,idc,jdc,kdc,ldc,mdc,ndc,odc,pdc,qdc,rdc,sdc,tdc,udc,vdc,wdc,xdc,ydc,zdc,Adc,Bdc,Cdc,Ddc,Edc,Fdc,Gdc,Hdc,Idc,Jdc,Kdc,Ldc,Mdc,Ndc,Odc,Pdc,Qdc,Rdc,Sdc,Tdc,Udc,Vdc,Wdc,Xdc,Ydc,Zdc,$dc,_dc,aec,bec,cec,dec,eec,fec,gec,hec,iec,jec,kec,lec,mec,nec,oec,pec,qec,rec,sec,tec,uec,vec,wec,xec,yec,zec,Aec,Bec,Cec,Dec,Eec,Fec,Gec,Hec,Iec,Jec,Kec,Lec,Mec,Nec,Oec,Pec,Qec,Rec,Sec,Tec,Uec,Vec,Wec,Xec,Yec,Zec,$ec,_ec,afc,bfc,cfc, +dfc,efc,ffc,gfc,hfc,ifc,jfc,kfc,lfc,mfc,nfc,ofc,pfc,qfc,rfc,sfc,tfc,ufc,vfc,wfc,xfc,yfc,zfc;var KU=lrc(10,XV,Dfc);var Ffc;IZ(23,1,yTc);_.ei=function fgc(){var a,b;return a=this.Fi(),b=a.fi(),!b?a:b};_.fi=function ggc(){return null};_.yd=function hgc(a){return Rfc(this,a)};_.gi=function jgc(a){var b;b=Ihc(this.fi());if(b)return Tfc(b,a);return null};_.hi=function kgc(a){return lgc(this,a)};_.ii=function mgc(a){var b,c;if(this==a)return this;a=igc(a);if(a.Mi())return Qoc(a.Mi(),this);return b=(c=new ukc(false), +Qfc(this,a,0,c)),b?this:igc(Zkc(this.A,CE(xE(cV,1),xPc,23,0,[this,a])))};_.ji=function ngc(a,b){return 0};_.ki=function ogc(){return this.B};_.li=function pgc(){return Vnc(this.B)};_.Ad=function qgc(){return Xfc(this)};_.mi=function rgc(){return false};_.ni=function sgc(){return false};_.oi=function tgc(){return false};_.pi=function vgc(){return false};_.qi=function wgc(){return false};_.ri=function ygc(){return false};_.si=function zgc(){return false};_.ti=function Agc(){return false};_.ui=function Bgc(){return false}; +_.vi=function Cgc(){return false};_.wi=function Dgc(){return false};_.xi=function Egc(){return false};_.yi=function Fgc(){return false};_.zi=function Ggc(a){return Igc(this,a,new Akc(true),0)};_.Ai=function Hgc(a,b,c){return Igc(this,a,b,c)};_.Bi=function Jgc(){return false};_.Ci=function Kgc(){return false};_.Di=function Lgc(){return false};_.Fi=function Mgc(){return this};_.Gi=function Ngc(){return null};_.Hi=function Ogc(){return null};_.Ii=function Pgc(){return null};_.Ji=function Rgc(){return null}; +_.Ki=function Sgc(){return null};_.Li=function Tgc(){return null};_.Mi=function Ugc(){return null};_.Bd=function Vgc(){return this.di(new stc,false).a};_.v=false;_.w=false;var Nfc;var cV=krc(23);IZ(1009,23,yTc,Wgc);_.di=function Xgc(a,b){return a.a+="*",a};_.mi=function Ygc(){return true};_.wi=function Zgc(){return true};_.Di=function $gc(){return true};_.Ei=function _gc(){return DKc(this)};_.Ni=function ahc(a){return _kc(a.e,57)};var LU=krc(1009);IZ(213,23,{213:1,23:1,3:1},ghc,hhc);_.di=function ihc(a, +b){return a.a+="[ArrowType]",a};_.hi=function jhc(a){throw gZ(new ytc);};_.ii=function khc(a){throw gZ(new ytc);};_.Oi=function lhc(){return Wfc(this.b)||dhc(this)};_.li=function(){return this.Oi()};_.zi=function mhc(a){return fhc(this,a,new Akc(true),0)};_.Ai=function nhc(a,b,c){return fhc(this,a,b,c)};_.Ei=function ohc(){var a,b;a=lFc(this.b);if(this.a){b=this.a.c;while(b){a=a*31+lFc(b.d);b=b.f}}return a};_.Ni=function phc(a){throw gZ(new ytc);};_.c=false;var MU=krc(213);IZ(211,23,yTc);_.ji=function rhc(a, +b){return b&&!!this.fi()?this.fi().ji(a,b):0};_.Ei=function shc(){return DKc(this)};var MV=krc(211);IZ(1003,211,yTc,thc);_.di=function uhc(a,b){return a.a+=XKc,a};_.fi=function vhc(){return _kc(this.A,9)};_.Ni=function whc(a){return _kc(a.e,8)};var NU=krc(1003);IZ(48,23,zTc);_.Pi=function Jhc(){this.u=true};_.gi=function Khc(a){return this.ji(a,false)==0?null:this.Wi(a)};_.Ri=function Lhc(){return bn(),mz(),lz};_.Ti=function Mhc(){return this.Vi().b.we()};_.Ui=function Nhc(){return null};_.ji=function Ohc(a, +b){return this.ti()||this.ri()||this.si()||this==this.A.u[64]||this.Bi()||!!this.Yi(a)?1:0};_.Vi=function Phc(){return _mc(),_mc(),$mc};_.Wi=function Qhc(a){return Chc(this,a)};_.Yi=function Rhc(a){return fnc(this.Vi(),a)};_.Zi=function Shc(){var a;a=this.Si();if(!a)return null;a=a.Si();return!a?null:a.Qi()};_.$i=function Thc(){return!this.u};_.qi=function Uhc(){return!!this.Ui()};_._i=function Vhc(){return false};_.xi=function Whc(){return true};_.yi=function Yhc(){var a;a=this.Qi();return!!a&&a.f== +(dkc(),bkc)&&a.e};_.Bi=function Zhc(){return Fhc(this)};_.aj=function $hc(a){};_.Ni=function _hc(a){return slc(a,this)};_.u=true;var jV=krc(48);IZ(1031,48,zTc,cic);_.di=function dic(a,b){if(b)return otc(a,this.d);return ptc(otc(ptc(ptc(a,this.b),"<"),this.d),">")};_.fi=function eic(){return this.d.fi()};_.gi=function fic(a){return Tfc(this.d,a)};_.Qi=function gic(){return!this.c?null:this.c.Qi()};_.Si=function hic(){return null};_.ji=function iic(a,b){return this.d.ji(a,b)};_.bj=function jic(){return!this.c? +(_mc(),_mc(),$mc):this.c.Vi()};_.Vi=function(){return this.bj()};_.Xi=function kic(){return this.b};_.ui=function lic(){return this.b!=null};_.wi=function mic(){return this.d.wi()};_.xi=function nic(){return this.d.xi()};_.zi=function oic(a){return aic(this,a,new Akc(true),0)};_.Ai=function pic(a,b,c){return aic(this,a,b,c)};_.Di=function qic(){return this.d.Di()};_.Ei=function ric(){return hmc(this)};_.Gi=function sic(){return this};_.Ni=function tic(a){return this};var OU=krc(1031);IZ(68,1,{},Lic); +_.c=null;_.d=null;_.e=0;_.g=null;_.i=null;_.j=null;_.k=null;_.n=null;var PU=krc(68);IZ(310,1,{},Qic);var QU=krc(310);IZ(76,48,ATc,Uic,Vic,Wic);_.di=function Yic(a,b){return Ric(this,a,b)};_.cj=function Zic(a,b,c,d){return Sic(this,a,b,c,d)};_.Qi=function $ic(){return null};_.Ri=function _ic(){return this.r?this.r.c:(Bn(),Bn(),An)};_.dj=function ajc(){return this.r?tjc(this.r):(Bn(),Bn(),An)};_.Si=function bjc(){return this.p};_.Ui=function cjc(){return this.r};_.Vi=function djc(){return this.t};_.Xi= +function ejc(){return this.o!=null?this.o:this.r?this.r.Xi()+fQc:null};_._i=function fjc(){return this.q};_.zi=function gjc(a){return this.Ai(a,new Akc(true),0)};_.Ai=function hjc(a,b,c){var d,e,f,g,h;if(Igc(this,a,b,c))return true;if(a.Mi())return false;if(a.Ji())return ijc(this,a.Ji(),b,c);e=pF(a,48)?a:null;d=!e?null:e.Qi();if(!!this.Qi()&&this.Qi().f==(dkc(),bkc))for(g=this.Ri().Vd();g.Id();){f=g.Jd();if(f.Ai(a,b,c))return true}else if(!!d&&d.f==(dkc(),bkc)){h=this.dj();for(g=h.Vd();g.Id();){f= +g.Jd();if(f.Ai(a,b,c))return true}}if(Fhc(this))return true;return!!e&&Dhc(this,e)};_.Ei=function jjc(){return this.yi()?Pxc(CE(xE(iW,1),mLc,1,5,[this.o,this.t])):DKc(this)};_.aj=function kjc(a){Xd(!this.r||!a);this.r=a};_.n=false;_.q=false;_.r=null;_.s=false;var mV=krc(76);IZ(128,76,BTc,Ljc);_.di=function Njc(a,b){var c,d,e;if(!this.s||this==this.A.u[17])return a.a+=b?"!Function":nPc,a;this.s=false;a.a+="function(";e=C8b(this.b.a);c=!pF(this.k,212);if(c){this.oi()?(a.a+="new:",a):(a.a+="this:",a); +this.k.di(a,b)}if(e>0){c&&(a.a+=iLc,a);d=this.b.a.c;E8b(d,(Hbc(),Ebc))!=0?njc(a,d.d,b):E8b(d,qbc)!=0?mjc(this,a,d.d,b):Pfc(d.d,a,b);d=d.f;while(d){a.a+=iLc;E8b(d,Ebc)!=0?njc(a,d.d,b):E8b(d,qbc)!=0?mjc(this,a,d.d,b):Pfc(d.d,a,b);d=d.f}}a.a+="): ";Pfc(this.b.b,a,b);this.s=true;return a};_.Pi=function Ojc(){pjc(this)};_.cj=function Pjc(a,b,c,d){var e;if(Esc(aNc,a)){e=pF(b,48)?b:null;if(e){if(!!this.g&&Zfc(e,this.g.e))return true;Hjc(this,e,d);return true}else return false}return Sic(this,a,b,c,d)};_.Ti= +function Qjc(){var a;if(!this.g)return this.t.b.we();else{a=(bn(),new Rs);Ywc(a.b,Sd(aNc));Qs(a,this.t.b.we());return np(new wxc(a.b))}};_.Wi=function Rjc(a){return yjc(this,a)};_.Yi=function Sjc(a){return Ajc(this,a)};_.Zi=function Tjc(){return Bjc(this)};_.li=function Ujc(){return Xnc(this.B)>0||Wfc(this.k)||Wfc(this.b)};_.$i=function Vjc(){return!!this.g||!this.u};_.oi=function Wjc(){return this.f==(dkc(),akc)};_.zi=function Xjc(a){return this.Ai(a,new Akc(true),0)};_.Ai=function Yjc(a,b,c){var d, +e;if(Igc(this,a,b,c))return true;if(a.Ii()){d=a.Ii();if(d.f==(dkc(),bkc))return true;if(this.f==bkc)return false;return e=!!cgc(d.k)&&!!cgc(d.k).Qi()&&cgc(d.k).Qi().f==bkc||d.k.Ai(this.k,b,0)||this.k.Ai(d.k,b,0),e&&fhc(this.b,d.b,b,c)}return this.A.u[18].Ai(a,b,c)};_.Ei=function Zjc(){var a;a=NKc(this.f);switch(this.f.f){case 1:return 31*a+DKc(this);case 2:return 31*a+TKc(this.Xi());case 0:a=31*a+Xfc(this.k);a=31*a+Xfc(this.b);return a;default:throw gZ(new Jqc);}};_.Ii=function $jc(){return this}; +_.Ni=function _jc(a){return rlc(a,this)};_.e=false;var SU=krc(128);IZ(246,12,{246:1,3:1,20:1,12:1},ekc);var akc,bkc,ckc;var RU=lrc(246,XV,fkc);IZ(311,76,ATc,gkc,hkc);_.di=function ikc(a,b){var c,d,e;if(this.a.Xi()!=null){if(b)return ptc(a,Ahc(this.a))}else return Ric(this,a,b);e=this.a.Xi();if(e.length==0){d=this.a.i;return ptc(ktc(ptc(ptc((a.a+="")}return a.a+=""+e,a};_.cj=function jkc(a,b,c,d){var e,f;e=zjc(this.a); +if(!!e&&(cnc(e.Vi(),a)?1:0)!=0&&(f=e.Yi(a),!!f&&!f.b))return false;return Sic(this,a,b,c,d)};_.Qi=function kkc(){return this.a};_.Ri=function lkc(){return this.a.c};_.dj=function mkc(){return tjc(this.a)};_.Si=function nkc(){return zjc(this.a)};_.Xi=function okc(){return this.a.Xi()};_.ui=function pkc(){return this.a.Xi()!=null};_.Ei=function qkc(){return this.a.Xi()!=null?hmc(this):Xfc(this)};var TU=krc(311);IZ(494,1,{});_.c=false;var YU=krc(494);IZ(73,494,{},ukc);var VU=krc(73);IZ(368,1,{368:1}, +vkc);_.yd=function wkc(a){var b;b=a;if(this===a)return true;return uF(this.b)===uF(b.b)&uF(this.c)===uF(b.c)|uF(this.b)===uF(b.c)&uF(this.c)===uF(b.b)};_.Ad=function xkc(){return this.a};_.a=0;var UU=krc(368);IZ(91,494,{},Akc);var XU=krc(91);IZ(293,1,{293:1},Bkc);_.yd=function Ckc(a){var b;b=a;if(this.b==b.b&&this.c==b.c)return true;return ykc(this.d,this.b,b.b)&&ykc(this.d,this.c,b.c)};_.Ad=function Dkc(){return this.a};_.a=0;var WU=krc(293);IZ(243,12,{243:1,3:1,20:1,12:1},Ikc);_.a=false;var Ekc, +Fkc,Gkc;var ZU=lrc(243,XV,Jkc);var $U=lrc(null,XV,Kkc);IZ(120,1,{120:1,3:1},llc);var bV=krc(120);IZ(890,128,BTc,nlc);_.Qi=function olc(){return this.A.u[16]};var _U=krc(890);IZ(891,1,BLc,plc);_.Od=function qlc(a){return new tEc};var aV=krc(891);IZ(969,1,{});_.ej=function xlc(a){return a};_.g=false;var dV=krc(969);IZ(138,48,FTc);_.di=function Blc(a,b){return this.e.di(a,b)};_.gi=function Clc(a){return Tfc(this.e,a)};_.Qi=function Dlc(){return!this.d?null:this.d.Qi()};_.Ri=function Elc(){return!this.d? +(Cyc(),Cyc(),Ayc):this.d.Ri()};_.Si=function Flc(){return!this.d?null:this.d.Si()};_.Ui=function Glc(){return!this.d?null:this.d.Ui()};_.ji=function Hlc(a,b){return this.e.ji(a,b)};_.Vi=function Ilc(){return!this.d?(_mc(),_mc(),$mc):this.d.Vi()};_.Xi=function Jlc(){return!this.d?"":this.d.Xi()};_.ki=function Klc(){return this.e.ki()};_.Oi=function Llc(){return Wfc(this.e)};_.li=function(){return this.Oi()};_.mi=function Mlc(){return this.e.mi()};_.ni=function Nlc(){return this.e.ni()};_.oi=function Olc(){return this.e.oi()}; +_._i=function Plc(){return!!this.d&&this.d._i()};_.ri=function Qlc(){return this.e.ri()};_.si=function Rlc(){return this.e.si()};_.ti=function Slc(){return this.e.ti()};_.ui=function Tlc(){return this.e.ui()};_.wi=function Ulc(){return this.e.wi()};_.yi=function Vlc(){return this.e.yi()};_.zi=function Wlc(a){return this.e.Ai(a,new Akc(true),0)};_.Ai=function Xlc(a,b,c){return this.e.Ai(a,b,c)};_.Bi=function Ylc(){return this.e.Bi()};_.Di=function Zlc(){return this.e.Di()};_.Ei=function $lc(){return Xfc(this.e)}; +_.Gi=function _lc(){return this.e.Gi()};_.Hi=function amc(){return this.e.Hi()};_.Ii=function bmc(){return this.e.Ii()};_.Ji=function cmc(){return this.e.Ji()};_.Ki=function dmc(){return this.e.Ki()};_.Li=function emc(){return this.e.Li()};_.Mi=function fmc(){return this.e.Mi()};_.Ni=function gmc(a){return tlc(a,this)};var nV=krc(138);IZ(635,128,BTc,imc);_.di=function jmc(a,b){return a.a+=b?"?":"NoObject",a};_.cj=function kmc(a,b,c,d){return true};_.Qi=function lmc(){return null};_.Si=function mmc(){return null}; +_.Xi=function nmc(){return null};_.oi=function omc(){return false};_.ri=function pmc(){return true};_.zi=function qmc(a){return this.Ai(a,new Akc(true),0)};_.Ai=function rmc(a,b,c){return Igc(this,a,b,c)||a.xi()&&!a.ti()&&!a.si()};_.Ei=function smc(){return DKc(this)};_.Ii=function tmc(){return null};_.Ni=function umc(a){return _kc(a.e,59)};var eV=krc(635);IZ(636,635,BTc,vmc);_.di=function wmc(a,b){return a.a+=b?"?":"None",a};_.ri=function xmc(){return false};_.ti=function ymc(){return true};_.wi= +function zmc(){return true};_.zi=function Amc(a){return this.Ai(a,null,0)};_.Ai=function Bmc(a,b,c){return true};_.Di=function Cmc(){return true};_.Ni=function Dmc(a){return this};var gV=krc(636);IZ(1010,636,BTc,Fmc);_.di=function Gmc(a,b){return a.a+=b?"?":"NoResolvedType",a};_.Xi=function Hmc(){return this.a};_.si=function Imc(){return true};_.ti=function Jmc(){return false};_.zi=function Kmc(a){return Emc(this,a,new Akc(true),0)};_.Ai=function Lmc(a,b,c){return Emc(this,a,b,c)};var fV=krc(1010); +IZ(1004,211,yTc,Mmc);_.di=function Nmc(a,b){return a.a+=jLc,a};_.vi=function Omc(){return true};_.wi=function Pmc(){return true};_.Fi=function Qmc(){return _kc(this.A,58)};_.Ni=function Rmc(a){return _kc(a.e,32)};var hV=krc(1004);IZ(1005,211,yTc,Smc);_.di=function Tmc(a,b){return a.a+=YKc,a};_.fi=function Umc(){return _kc(this.A,34)};_.Ni=function Vmc(a){return _kc(a.e,33)};var iV=krc(1005);IZ(312,1,{312:1,3:1},Xmc);_.Ad=function Ymc(){return Pxc(CE(xE(iW,1),mLc,1,5,[this.c,this.e]))};_.Bd=function Zmc(){return"Property { name: "+ +this.c+", type:"+this.e+", inferred: "+this.b+"}"};_.a=null;_.b=false;var lV=krc(312);IZ(423,1,wNc,inc,jnc);_.Ad=function knc(){return lFc(this.b.we())};_.a=null;var $mc;var kV=krc(423);IZ(1029,76,ATc,onc);_.cj=function pnc(a,b,c,d){return lnc(this,a,b,c,d)};_.Si=function qnc(){return this.A.u[38]};_.yi=function rnc(){return true};_.zi=function snc(a){return nnc(this,a,new Akc(true),0)};_.Ai=function tnc(a,b,c){return nnc(this,a,b,c)};_.Ji=function unc(){return this};_.a=false;var qV=krc(1029);IZ(420, +1,{},xnc);_.a=true;_.b=true;var pV=krc(420);IZ(421,1,{421:1},ync);_.Bd=function znc(){return"RecordProperty{type: "+this.b+", node: "+this.a+"}"};var oV=krc(421);IZ(1006,211,yTc,Anc);_.di=function Bnc(a,b){return a.a+=$Kc,a};_.fi=function Cnc(){return _kc(this.A,43)};_.Ni=function Dnc(a){return _kc(a.e,45)};var rV=krc(1006);IZ(1007,211,yTc,Enc);_.di=function Fnc(a,b){return a.a+=sRc,a};_.fi=function Gnc(){return _kc(this.A,46)};_.Ni=function Hnc(a){return _kc(a.e,48)};var sV=krc(1007);IZ(62,138,{23:1, +48:1,138:1,62:1,3:1},Inc);_.di=function Jnc(a,b){return ptc(a,this.a)};_.Xi=function Knc(){return this.a};_.Oi=function Lnc(){return true};_.Ki=function Mnc(){return this};_.Ni=function Nnc(a){return a.ej(this)};var vV=krc(62);IZ(392,1,wNc,$nc);_.Bd=function boc(){var a,b,c;c="";b=this.c.Yd();c+="{ ";for(a=0;a=0?":"+this.c:"")+")"};_.c=0;var kW=krc(143);lF={3:1,432:1,20:1,2:1};var nW=krc(2);IZ(30,261,{432:1,30:1},stc,ttc,utc,vtc);var lW=krc(30);IZ(327,125,{3:1,38:1,25:1,327:1,36:1},wtc);var mW=krc(327);IZ(1150,1,{});IZ(28,25,{3:1,38:1,25:1,36:1,28:1},ytc,ztc);var pW=krc(28);IZ(141,12,{3:1,20:1,12:1,141:1},Jtc);var Atc,Btc,Ctc,Dtc,Etc,Ftc,Gtc,Htc;var qW=lrc(141,XV,Ktc);IZ(192,1,JTc);_.ie=function Ltc(a){return Bsc(this.a,a.a)};_.yd=function Mtc(a){var b; +if(a===this)return true;if(!pF(a,192))return false;b=a;return Esc(this.a,b.a)};_.Ad=function Ntc(){return TKc(this.a)};_.Bd=function Otc(){return this.a};var rW=krc(192);IZ(6,1,{},Rtc);var sW=krc(6);IZ(232,1064,yLc);_.Rd=function guc(){Ttc(this)};_.se=function huc(a){return Utc(this,a)};_.Ge=function iuc(a){return Vtc(this,a)};_.He=function juc(){return new puc(this)};_.Ie=function kuc(a){return Xtc(this,a)};_.Je=function luc(a,b){return $tc(this,a,b)};_.Ke=function muc(a){return auc(this,a)};_.Yd= +function nuc(){return duc(this)};var wW=krc(232);IZ(131,zLc,ALc,puc);_.Rd=function quc(){this.a.Rd()};_.Sd=function ruc(a){return ouc(this,a)};_.Vd=function suc(){return new yuc(this.a)};_.Wd=function tuc(a){var b;if(ouc(this,a)){b=a.Ve();this.a.Ke(b);return true}return false};_.Yd=function uuc(){return this.a.Yd()};var vW=krc(131);IZ(157,1,cLc,yuc);_.Hd=function zuc(a){lEc(this,a)};_.Jd=function Buc(){return wuc(this)};_.Id=function Auc(){return this.b};_.Kd=function Cuc(){xuc(this)};_.b=false;var uW= +krc(157);IZ(103,1,cLc,Guc);_.Hd=function Huc(a){lEc(this,a)};_.Id=function Iuc(){return Duc(this)};_.Jd=function Juc(){return Euc(this)};_.Kd=function Kuc(){Fuc(this)};_.b=0;_.c=-1;var xW=krc(103);IZ(328,103,ELc,Muc);_.Kd=function Quc(){Fuc(this)};_.oe=function Nuc(a){this.a.be(this.b,a);++this.b;this.c=-1};_.pe=function Ouc(){return this.b>0};_.qe=function Puc(){return Luc(this)};var yW=krc(328);IZ(455,nLc,oLc,Ruc);_.be=function Suc(a,b){this.c.be(this.a+a,b);++this.b};_.ce=function Tuc(a){return this.c.ce(this.a+ +a)};_.fe=function Uuc(a){var b;b=this.c.fe(this.a+a);--this.b;return b};_.Yd=function Vuc(){return this.b};_.a=0;_.b=0;var zW=krc(455);IZ(57,zLc,ALc,Wuc);_.Rd=function Xuc(){this.a.Rd()};_.Sd=function Yuc(a){return this.a.se(a)};_.Vd=function Zuc(){var a;return a=this.a.He().Vd(),new avc(a)};_.Wd=function $uc(a){if(this.a.se(a)){this.a.Ke(a);return true}return false};_.Yd=function _uc(){return this.a.Yd()};var CW=krc(57);IZ(74,1,cLc,avc);_.Hd=function bvc(a){lEc(this,a)};_.Id=function cvc(){return this.a.Id()}; +_.Jd=function dvc(){var a;return a=this.a.Jd(),a.Ve()};_.Kd=function evc(){this.a.Kd()};var BW=krc(74);IZ(110,kLc,lLc,fvc);_.Rd=function gvc(){this.a.Rd()};_.Sd=function hvc(a){return this.a.Ge(a)};_.Vd=function ivc(){var a;return a=this.a.He().Vd(),new kvc(a)};_.Yd=function jvc(){return this.a.Yd()};var EW=krc(110);IZ(121,1,cLc,kvc);_.Hd=function lvc(a){lEc(this,a)};_.Id=function mvc(){return this.a.Id()};_.Jd=function nvc(){var a;return a=this.a.Jd(),a.We()};_.Kd=function ovc(){this.a.Kd()};var DW= +krc(121);IZ(233,1,{233:1,50:1});_.yd=function qvc(a){var b;if(!pF(a,50))return false;b=a;return kFc(this.d,b.Ve())&&kFc(this.e,b.We())};_.Ve=function rvc(){return this.d};_.We=function svc(){return this.e};_.Ad=function tvc(){return lFc(this.d)^lFc(this.e)};_.Xe=function uvc(a){return pvc(this,a)};_.Bd=function vvc(){return this.d+"="+this.e};var FW=krc(233);IZ(170,233,{233:1,170:1,50:1},wvc);var GW=krc(170);IZ(1077,1,SLc);_.yd=function xvc(a){var b;if(!pF(a,50))return false;b=a;return kFc(this.Ve(), +b.Ve())&&kFc(this.We(),b.We())};_.Ad=function yvc(){return lFc(this.Ve())^lFc(this.We())};_.Bd=function zvc(){return this.Ve()+"="+this.We()};var HW=krc(1077);IZ(1080,1064,yLc);_.Fe=function Cvc(a){return Avc(this,a)};_.se=function Dvc(a){return Bvc(this,a)};_.uj=function Evc(){return new Ivc(this)};_.He=function Fvc(){return new Tvc(this)};_.Ie=function Gvc(a){var b;return b=a,Hg(this.wj(b))};_.we=function Hvc(){return new Yvc(this)};var NW=krc(1080);IZ(765,1080,yLc,Ivc);_.Rd=function Jvc(){this.a.Rd()}; +_.Pe=function Kvc(){return Iyc(this.a.Pe())};_.tj=function Lvc(){return this.a.vj()};_.uj=function Mvc(){return this.a};_.vj=function Nvc(){return this.a.tj()};_.wj=function Ovc(a){return this.a.wj(a)};_.Je=function Pvc(a,b){return this.a.Je(a,b)};_.Ke=function Qvc(a){return this.a.Ke(a)};_.xj=function Rvc(a){return this.a.xj(a)};_.Yd=function Svc(){return this.a.Yd()};var JW=krc(765);IZ(289,zLc,ALc,Tvc);_.Sd=function Uvc(a){return pF(a,50)&&Avc(this.b,a)};_.Vd=function Vvc(){return this.b.vj()}; +_.Wd=function Wvc(a){var b;if(pF(a,50)){b=a;return this.b.xj(b)}return false};_.Yd=function Xvc(){return this.b.Yd()};var KW=krc(289);IZ(116,zLc,GLc,Yvc);_.Zd=function dwc(){return new BFc(this)};_.Rd=function Zvc(){this.a.Rd()};_.Pe=function $vc(){return this.a.Pe()};_.Sd=function _vc(a){return Bvc(this.a,a)};_.Vd=function awc(){var a;return a=this.a.He().b.vj(),new ewc(a)};_.Wd=function bwc(a){if(Bvc(this.a,a)){this.a.Ke(a);return true}return false};_.Yd=function cwc(){return this.a.Yd()};var MW= +krc(116);IZ(117,1,cLc,ewc);_.Hd=function fwc(a){lEc(this,a)};_.Id=function gwc(){return this.a.Id()};_.Jd=function hwc(){var a;return a=this.a.Jd(),a.Ve()};_.Kd=function iwc(){this.a.Kd()};var LW=krc(117);IZ(65,kLc,lLc,Cwc,Ewc);_.Pd=function Fwc(a){return lwc(this,a),true};_.Rd=function Hwc(){mwc(this)};_.Sd=function Iwc(a){return nwc(new Rwc(this),a)};_.Ud=function Jwc(){return swc(this)};_.Vd=function Kwc(){return new Rwc(this)};_.Wd=function Lwc(a){return xwc(new Rwc(this),a)};_.Yd=function Mwc(){return Bwc(this)}; +_.Zd=function Nwc(){return new tFc(this,272)};_.ae=function Owc(a){var b;b=this.c-this.b&this.a.length-1;a.lengthb&&(a[b]=null);return a};_.b=0;_.c=0;var RW=krc(65);IZ(161,1,cLc,Rwc);_.Hd=function Swc(a){lEc(this,a)};_.Id=function Twc(){return this.a!=this.b};_.Jd=function Uwc(){return Pwc(this)};_.Kd=function Vwc(){Qwc(this)};_.a=0;_.b=0;_.c=-1;var QW=krc(161);IZ(16,nLc,{3:1,15:1,16:1,21:1,54:1,69:1},fxc,gxc,hxc);_.be=function ixc(a,b){Xwc(this, +a,b)};_.Pd=function jxc(a){return Ywc(this,a)};_.Qd=function kxc(a){return Zwc(this,a)};_.Rd=function lxc(){this.a=zE(iW,mLc,1,0,5,1)};_.Sd=function mxc(a){return _wc(this,a,0)!=-1};_.ce=function nxc(a){return $wc(this,a)};_.Ud=function oxc(){return this.a.length==0};_.Vd=function pxc(){return new wxc(this)};_.fe=function qxc(a){return axc(this,a)};_.Wd=function rxc(a){return bxc(this,a)};_.ge=function sxc(a,b){var c;c=b-a;qKc(this.a,a,c)};_.Yd=function txc(){return this.a.length};_._d=function uxc(){return lKc(this.a, +this.a.length)};_.ae=function vxc(a){return exc(this,a)};var TW=krc(16);IZ(27,1,cLc,wxc);_.Hd=function xxc(a){lEc(this,a)};_.Id=function yxc(){return this.a=0?"+":"")+(c/60|0);b=_Bc($wnd.Math.abs(c)%60);return(dCc(),bCc)[this.a.getDay()]+" "+cCc[this.a.getMonth()]+" "+_Bc(this.a.getDate())+" "+_Bc(this.a.getHours())+":"+_Bc(this.a.getMinutes())+ +":"+_Bc(this.a.getSeconds())+" GMT"+a+b+" "+this.a.getFullYear()};var sX=krc(294);var bCc,cCc;IZ(201,1064,{201:1,51:1},kCc,lCc);_.Je=function rCc(a,b){return hCc(this,a,b)};_.Rd=function mCc(){eCc(this)};_.se=function nCc(a){return PCc(this.a,a)};_.Ge=function oCc(a){var b,c;for(c=new aDc(this.a);c.a0};_.Kd=function OGc(){Fuc(this.a);sGc(this.c,this.b);this.b=null};var dY=krc(351);IZ(288,1,cLc,PGc,QGc);_.Hd=function RGc(a){lEc(this,a)};_.Jd=function TGc(){return this.b=Euc(this.a)};_.Id=function SGc(){return Duc(this.a)};_.Kd=function UGc(){Fuc(this.a);sGc(this.c,this.b);this.b=null};var eY=krc(288);IZ(352,289,ALc,VGc);_.Rd=function WGc(){jGc(this.a)};var fY=krc(352);IZ(198,170,{233:1,170:1,50:1,198:1},XGc);_.b=false;var gY=krc(198);IZ(353,1,{}, +YGc);_.Bd=function ZGc(){return"State: mv="+this.c+" value="+this.d+" done="+this.a+" found="+this.b};_.a=false;_.b=false;_.c=false;var hY=krc(353);IZ(761,1080,yLc,aHc);_.Pe=function bHc(){return IBc(this.c.a)};_.tj=function cHc(){return new KGc(this.c,this.f,this.b,this.a,this.e,this.d)};_.vj=function dHc(){return new QGc(this.c,this.f,this.b,this.a,this.e,this.d)};_.He=function eHc(){return new Tvc(this)};_.wj=function fHc(a){return $Gc(this,kGc(this.c,a))};_.Je=function gHc(a,b){if(!nGc(this.c, +this.f,a,this.b,this.a,this.e,this.d))throw gZ(new qnb(a+" outside the range "+this.b+" to "+this.e));return qGc(this.c,a,b)};_.Ke=function hHc(a){var b;b=a;if(!nGc(this.c,this.f,b,this.b,this.a,this.e,this.d))return null;return rGc(this.c,b)};_.xj=function iHc(a){return _Gc(this,a.Ve())&&sGc(this.c,a)};_.Yd=function jHc(){var a,b,c;this.a?b=lGc(this.c,this.b,true):b=lGc(this.c,this.b,false);if(!(!!b&&_Gc(this,b.d)?b:null))return 0;a=0;for(c=new QGc(this.c,this.f,this.b,this.a,this.e,this.d);Duc(c.a);c.b= +Euc(c.a))++a;return a};_.a=false;_.d=false;var mY=krc(761);IZ(148,12,NTc,pHc);_.Bj=function qHc(){return false};_.Cj=function rHc(){return false};var kHc,lHc,mHc,nHc;var lY=lrc(148,XV,sHc);IZ(762,148,NTc,tHc);_.Cj=function uHc(){return true};var iY=lrc(762,lY,null);IZ(763,148,NTc,vHc);_.Bj=function wHc(){return true};_.Cj=function xHc(){return true};var jY=lrc(763,lY,null);IZ(764,148,NTc,yHc);_.Bj=function zHc(){return true};var kY=lrc(764,lY,null);IZ(102,zLc,{3:1,15:1,21:1,316:1,35:1,154:1},CHc, +DHc,EHc);_.Zd=function MHc(){return new BFc(this)};_.Pd=function FHc(a){return AHc(this,a)};_.Rd=function GHc(){this.a.Rd()};_.Pe=function HHc(){return this.a.Pe()};_.Sd=function IHc(a){return Bvc(this.a,a)};_.Vd=function JHc(){var a;return a=(new Yvc(this.a)).a.He().b.vj(),new ewc(a)};_.Wd=function KHc(a){return BHc(this,a)};_.Yd=function LHc(){return this.a.Yd()};var oY=krc(102);IZ(217,1064,yLc,RHc,SHc);_.se=function THc(a){return NHc(this,a)};_.Ge=function UHc(a){if(a==null)throw gZ(new msc);return Vtc(this.a, +a)};_.He=function VHc(){return new puc(this.a)};_.Ie=function WHc(a){return OHc(this,a)};_.Je=function XHc(a,b){return PHc(this,a,b)};_.Ke=function YHc(a){if(a==null)throw gZ(new msc);return auc(this.a,a)};var pY=krc(217);IZ(277,1,{277:1});var qY=krc(277);IZ(1079,1,wNc);_.ug=function cIc(){return"DUMMY"};_.qj=function dIc(){return-1};_.Bd=function eIc(){return this.ug()};var ZHc,$Hc,_Hc,aIc;var vY=krc(1079);IZ(746,1079,wNc,fIc);_.ug=function gIc(){return"FINE"};_.qj=function hIc(){return 500};var rY= +krc(746);IZ(747,1079,wNc,iIc);_.ug=function jIc(){return"INFO"};_.qj=function kIc(){return 800};var sY=krc(747);IZ(748,1079,wNc,lIc);_.ug=function mIc(){return"SEVERE"};_.qj=function nIc(){return 1E3};var tY=krc(748);IZ(749,1079,wNc,oIc);_.ug=function pIc(){return AMc};_.qj=function qIc(){return 900};var uY=krc(749);IZ(730,1,{},uIc);var rIc;var wY=krc(730);IZ(861,1,wNc,wIc);_.c=null;var xY=krc(861);IZ(276,1,{276:1},QIc);_.e=false;var xIc=false,yIc=false,zIc=false,AIc=false,BIc=false;var yY=krc(276); +IZ(472,277,{277:1},TIc);var zY=krc(472);IZ(860,1,{},VIc);var AY=krc(860);IZ(329,1,{329:1},XIc);var BY=krc(329);IZ(292,1,{},$Ic);var CY=krc(292);IZ(889,1,BLc,_Ic);_.Od=function aJc(a){return fGc(a)};var DY=krc(889);IZ(888,1,{},bJc);_.Ue=function cJc(a,b){eGc(a,b)};var EY=krc(888);IZ(887,1,{},dJc);_.Cd=function eJc(){return new hGc(this.a,this.b,this.c)};var FY=krc(887);IZ(934,933,KLc,iJc);_.Aj=function jJc(a){return hJc(this,a)};_.a=0;_.b=0;_.c=0;var GY=krc(934);IZ(359,1,{});_.d=false;var $Y=krc(359); +IZ(970,359,PTc,oJc);_.Dj=function pJc(a){return kJc(this,a)};_.Ej=function qJc(){return lJc(this),this.a};var IY=krc(970);IZ(600,359,PTc,rJc);_.Dj=function sJc(a){return kJc(this,a)};_.Ej=function tJc(){return lJc(this),TFc(),RFc};var HY=krc(600);var ZY=mrc();IZ(771,1,{1114:1},vJc);var JY=krc(771);IZ(772,1,{1114:1},wJc);var KY=krc(772);IZ(769,241,KLc,zJc);_.hf=function AJc(a){while(xJc(this))if(this.a.hf(a))return true;else this.a=null;return false};var MY=krc(769);IZ(770,1,{},BJc);_.jf=function CJc(a){yJc(this.a, +a)};var LY=krc(770);IZ(83,359,{1036:1,1040:1,1113:1},KJc);_.Dj=function NJc(a){return kJc(this,a)};var YY=krc(83);IZ(823,241,KLc,OJc);_.hf=function PJc(a){var b;if(!this.a){b=new fxc;this.b.a.Hd(new QJc(b));Cyc();dxc(b,this.c);this.a=new tFc(b,16)}return sFc(this.a,a)};_.a=null;var OY=krc(823);IZ(824,1,{},QJc);_.jf=function RJc(a){Ywc(this.a,a)};var NY=krc(824);IZ(815,241,KLc,TJc);_.hf=function UJc(a){this.b=false;while(!this.b&&this.c.hf(new VJc(this,a)));return this.b};_.b=false;var QY=krc(815); +IZ(819,1,{},VJc);_.jf=function WJc(a){SJc(this.a,this.b,a)};var PY=krc(819);IZ(814,241,KLc,YJc);_.hf=function ZJc(a){return this.b.hf(new $Jc(this,a))};var SY=krc(814);IZ(818,1,{},$Jc);_.jf=function _Jc(a){XJc(this.a,this.b,a)};var RY=krc(818);IZ(816,241,KLc,aKc);_.hf=function bKc(a){while(oZ(this.b,0)){if(!this.a.hf(new cKc))return false;this.b=vZ(this.b,1)}return this.a.hf(a)};_.b=0;var UY=krc(816);IZ(820,1,{},cKc);_.jf=function dKc(a){};var TY=krc(820);IZ(817,1,{},fKc);_.jf=function gKc(a){eKc(this, +a)};var VY=krc(817);IZ(821,1,{},iKc);var WY=krc(821);IZ(822,1,{},jKc);_.jf=function kKc(a){MJc(this.b,this.a,a)};var XY=krc(822);IZ(1148,1,{});IZ(935,1,{},wKc);var _Y=krc(935);IZ(452,192,JTc);var cZ=krc(452);IZ(453,452,JTc,AKc);var aZ=krc(453);IZ(699,452,JTc,CKc);var bZ=krc(699);IZ(1142,1,{});var MKc=0;var OKc,PKc=0,QKc;IZ(1194,1,{});var xF=nrc("C");var yF=nrc("I");var dZ=nrc("Z");var zF=nrc("J");var wF=nrc("B");sGb();_=MZ(NPc);_.transpile=vGb;var VKc=(TD(),WD);var gwtOnLoad=gwtOnLoad=EZ;CZ(PZ);FZ("permProps", +[[["locale",JNc],["user.agent","safari"]]]);this["$gwtExport"]=$wnd;$wnd=this;typeof gwtOnLoad==="function"&&gwtOnLoad()}).call(this&&this.self||(typeof window!=="undefined"?window:typeof global!=="undefined"?global:this),this&&this.self||(typeof window!=="undefined"?window:typeof global!=="undefined"?global:this)); diff --git a/closure-library/closure/goog/tweak/entries.js b/closure-library/closure/goog/tweak/entries.js new file mode 100644 index 0000000000..221a3892a6 --- /dev/null +++ b/closure-library/closure/goog/tweak/entries.js @@ -0,0 +1,1002 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Definitions for all tweak entries. + * The class hierarchy is as follows (abstract entries are denoted with a *): + * BaseEntry(id, description) * + * -> ButtonAction(buttons in the UI) + * -> BaseSetting(query parameter) * + * -> BooleanGroup(child booleans) + * -> BasePrimitiveSetting(value, defaultValue) * + * -> BooleanSetting + * -> StringSetting + * -> NumericSetting + * -> BooleanInGroupSetting(token) + * Most clients should not use these classes directly, but instead use the API + * defined in tweak.js. One possible use case for directly using them is to + * register tweaks that are not known at compile time. + * + * @author agrieve@google.com (Andrew Grieve) + */ + +goog.provide('goog.tweak.BaseEntry'); +goog.provide('goog.tweak.BasePrimitiveSetting'); +goog.provide('goog.tweak.BaseSetting'); +goog.provide('goog.tweak.BooleanGroup'); +goog.provide('goog.tweak.BooleanInGroupSetting'); +goog.provide('goog.tweak.BooleanSetting'); +goog.provide('goog.tweak.ButtonAction'); +goog.provide('goog.tweak.NumericSetting'); +goog.provide('goog.tweak.StringSetting'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.log'); +goog.require('goog.object'); + + + +/** + * Base class for all Registry entries. + * @param {string} id The ID for the entry. Must contain only letters, + * numbers, underscores and periods. + * @param {string} description A description of what the entry does. + * @constructor + */ +goog.tweak.BaseEntry = function(id, description) { + /** + * An ID to uniquely identify the entry. + * @type {string} + * @private + */ + this.id_ = id; + + /** + * A descriptive label for the entry. + * @type {string} + */ + this.label = id; + + /** + * A description of what this entry does. + * @type {string} + */ + this.description = description; + + /** + * Functions to be called whenever a setting is changed or a button is + * clicked. + * @type {!Array} + * @private + */ + this.callbacks_ = []; +}; + + +/** + * The logger for this class. + * @type {goog.log.Logger} + * @protected + */ +goog.tweak.BaseEntry.prototype.logger = + goog.log.getLogger('goog.tweak.BaseEntry'); + + +/** + * Whether a restart is required for changes to the setting to take effect. + * @type {boolean} + * @private + */ +goog.tweak.BaseEntry.prototype.restartRequired_ = true; + + +/** + * @return {string} Returns the entry's ID. + */ +goog.tweak.BaseEntry.prototype.getId = function() { + return this.id_; +}; + + +/** + * Returns whether a restart is required for changes to the setting to take + * effect. + * @return {boolean} The value. + */ +goog.tweak.BaseEntry.prototype.isRestartRequired = function() { + return this.restartRequired_; +}; + + +/** + * Sets whether a restart is required for changes to the setting to take + * effect. + * @param {boolean} value The new value. + */ +goog.tweak.BaseEntry.prototype.setRestartRequired = function(value) { + this.restartRequired_ = value; +}; + + +/** + * Adds a callback that should be called when the setting has changed (or when + * an action has been clicked). + * @param {!Function} callback The callback to add. + */ +goog.tweak.BaseEntry.prototype.addCallback = function(callback) { + this.callbacks_.push(callback); +}; + + +/** + * Removes a callback that was added by addCallback. + * @param {!Function} callback The callback to add. + */ +goog.tweak.BaseEntry.prototype.removeCallback = function(callback) { + goog.array.remove(this.callbacks_, callback); +}; + + +/** + * Calls all registered callbacks. + */ +goog.tweak.BaseEntry.prototype.fireCallbacks = function() { + for (var i = 0, callback; callback = this.callbacks_[i]; ++i) { + callback(this); + } +}; + + + +/** + * Base class for all tweak entries that are settings. Settings are entries + * that are associated with a query parameter. + * @param {string} id The ID for the setting. + * @param {string} description A description of what the setting does. + * @constructor + * @extends {goog.tweak.BaseEntry} + */ +goog.tweak.BaseSetting = function(id, description) { + goog.tweak.BaseEntry.call(this, id, description); + // Apply this restriction for settings since they turn in to query + // parameters. For buttons, it's not really important. + goog.asserts.assert( + !/[^A-Za-z0-9._]/.test(id), 'Tweak id contains illegal characters: ', id); + + /** + * The value of this setting's query parameter. + * @type {string|undefined} + * @protected + */ + this.initialQueryParamValue; + + /** + * The query parameter that controls this setting. + * @type {?string} + * @private + */ + this.paramName_ = this.getId().toLowerCase(); +}; +goog.inherits(goog.tweak.BaseSetting, goog.tweak.BaseEntry); + + +/** + * States of initialization. Entries are initialized lazily in order to allow + * their initialization to happen in multiple statements. + * @enum {number} + * @private + */ +goog.tweak.BaseSetting.InitializeState_ = { + // The start state for all settings. + NOT_INITIALIZED: 0, + // This is used to allow concrete classes to call assertNotInitialized() + // during their initialize() function. + INITIALIZING: 1, + // One a setting is initialized, it may no longer change its configuration + // settings (associated query parameter, token, etc). + INITIALIZED: 2 +}; + + +/** + * The logger for this class. + * @type {goog.log.Logger} + * @protected + * @override + */ +goog.tweak.BaseSetting.prototype.logger = + goog.log.getLogger('goog.tweak.BaseSetting'); + + +/** + * Whether initialize() has been called (or is in the middle of being called). + * @type {goog.tweak.BaseSetting.InitializeState_} + * @private + */ +goog.tweak.BaseSetting.prototype.initializeState_ = + goog.tweak.BaseSetting.InitializeState_.NOT_INITIALIZED; + + +/** + * Sets the value of the entry based on the value of the query parameter. Once + * this is called, configuration settings (associated query parameter, token, + * etc) may not be changed. + * @param {?string} value The part of the query param for this setting after + * the '='. Null if it is not present. + * @protected + */ +goog.tweak.BaseSetting.prototype.initialize = goog.abstractMethod; + + +/** + * Returns the value to be used in the query parameter for this tweak. + * @return {?string} The encoded value. Null if the value is set to its + * default. + */ +goog.tweak.BaseSetting.prototype.getNewValueEncoded = goog.abstractMethod; + + +/** + * Asserts that this tweak has not been initialized yet. + * @param {string} funcName Function name to use in the assertion message. + * @protected + */ +goog.tweak.BaseSetting.prototype.assertNotInitialized = function(funcName) { + goog.asserts.assert( + this.initializeState_ != + goog.tweak.BaseSetting.InitializeState_.INITIALIZED, + 'Cannot call ' + funcName + ' after the tweak as been initialized.'); +}; + + +/** + * Returns whether the setting is currently being initialized. + * @return {boolean} Whether the setting is currently being initialized. + * @protected + */ +goog.tweak.BaseSetting.prototype.isInitializing = function() { + return this.initializeState_ == + goog.tweak.BaseSetting.InitializeState_.INITIALIZING; +}; + + +/** + * Sets the initial query parameter value for this setting. May not be called + * after the setting has been initialized. + * @param {string} value The initial query parameter value for this setting. + */ +goog.tweak.BaseSetting.prototype.setInitialQueryParamValue = function(value) { + this.assertNotInitialized('setInitialQueryParamValue'); + this.initialQueryParamValue = value; +}; + + +/** + * Returns the name of the query parameter used for this setting. + * @return {?string} The param name. Null if no query parameter is directly + * associated with the setting. + */ +goog.tweak.BaseSetting.prototype.getParamName = function() { + return this.paramName_; +}; + + +/** + * Sets the name of the query parameter used for this setting. If null is + * passed the the setting will not appear in the top-level query string. + * @param {?string} value The new value. + */ +goog.tweak.BaseSetting.prototype.setParamName = function(value) { + this.assertNotInitialized('setParamName'); + this.paramName_ = value; +}; + + +/** + * Applies the default value or query param value if this is the first time + * that the function has been called. + * @protected + */ +goog.tweak.BaseSetting.prototype.ensureInitialized = function() { + if (this.initializeState_ == + goog.tweak.BaseSetting.InitializeState_.NOT_INITIALIZED) { + // Instead of having only initialized / not initialized, there is a + // separate in-between state so that functions that call + // assertNotInitialized() will not fail when called inside of the + // initialize(). + this.initializeState_ = + goog.tweak.BaseSetting.InitializeState_.INITIALIZING; + var value = this.initialQueryParamValue == undefined ? + null : + this.initialQueryParamValue; + this.initialize(value); + this.initializeState_ = goog.tweak.BaseSetting.InitializeState_.INITIALIZED; + } +}; + + + +/** + * Base class for all settings that wrap primitive values. + * @param {string} id The ID for the setting. + * @param {string} description A description of what the setting does. + * @param {*} defaultValue The default value for this setting. + * @constructor + * @extends {goog.tweak.BaseSetting} + */ +goog.tweak.BasePrimitiveSetting = function(id, description, defaultValue) { + goog.tweak.BaseSetting.call(this, id, description); + /** + * The default value of the setting. + * @type {?} + * @private + */ + this.defaultValue_ = defaultValue; + + /** + * The value of the tweak. + * @type {?} + * @private + */ + this.value_; + + /** + * The value of the tweak once "Apply Tweaks" is pressed. + * @type {?} + * @private + */ + this.newValue_; +}; +goog.inherits(goog.tweak.BasePrimitiveSetting, goog.tweak.BaseSetting); + + +/** + * The logger for this class. + * @type {goog.log.Logger} + * @protected + * @override + */ +goog.tweak.BasePrimitiveSetting.prototype.logger = + goog.log.getLogger('goog.tweak.BasePrimitiveSetting'); + + +/** + * Returns the query param encoded representation of the setting's value. + * @return {string} The encoded value. + * @protected + */ +goog.tweak.BasePrimitiveSetting.prototype.encodeNewValue = goog.abstractMethod; + + +/** + * If the setting has the restartRequired option, then returns its initial + * value. Otherwise, returns its current value. + * @return {?} The value. + */ +goog.tweak.BasePrimitiveSetting.prototype.getValue = function() { + this.ensureInitialized(); + return this.value_; +}; + + +/** + * Returns the value of the setting to use once "Apply Tweaks" is clicked. + * @return {?} The value. + */ +goog.tweak.BasePrimitiveSetting.prototype.getNewValue = function() { + this.ensureInitialized(); + return this.newValue_; +}; + + +/** + * Sets the value of the setting. If the setting has the restartRequired + * option, then the value will not be changed until the "Apply Tweaks" button + * is clicked. If it does not have the option, the value will be update + * immediately and all registered callbacks will be called. + * @param {?} value The value. + */ +goog.tweak.BasePrimitiveSetting.prototype.setValue = function(value) { + this.ensureInitialized(); + var changed = this.newValue_ != value; + this.newValue_ = value; + // Don't fire callbacks if we are currently in the initialize() method. + if (this.isInitializing()) { + this.value_ = value; + } else { + if (!this.isRestartRequired()) { + // Update the current value only if the tweak has been marked as not + // needing a restart. + this.value_ = value; + } + if (changed) { + this.fireCallbacks(); + } + } +}; + + +/** + * Returns the default value for this setting. + * @return {?} The default value. + */ +goog.tweak.BasePrimitiveSetting.prototype.getDefaultValue = function() { + return this.defaultValue_; +}; + + +/** + * Sets the default value for the tweak. + * @param {?} value The new value. + */ +goog.tweak.BasePrimitiveSetting.prototype.setDefaultValue = function(value) { + this.assertNotInitialized('setDefaultValue'); + this.defaultValue_ = value; +}; + + +/** + * @override + */ +goog.tweak.BasePrimitiveSetting.prototype.getNewValueEncoded = function() { + this.ensureInitialized(); + return this.newValue_ == this.defaultValue_ ? null : this.encodeNewValue(); +}; + + + +/** + * A registry setting for string values. + * @param {string} id The ID for the setting. + * @param {string} description A description of what the setting does. + * @constructor + * @extends {goog.tweak.BasePrimitiveSetting} + * @final + */ +goog.tweak.StringSetting = function(id, description) { + goog.tweak.BasePrimitiveSetting.call(this, id, description, ''); + /** + * Valid values for the setting. + * @type {Array|undefined} + */ + this.validValues_; +}; +goog.inherits(goog.tweak.StringSetting, goog.tweak.BasePrimitiveSetting); + + +/** + * The logger for this class. + * @type {goog.log.Logger} + * @protected + * @override + */ +goog.tweak.StringSetting.prototype.logger = + goog.log.getLogger('goog.tweak.StringSetting'); + + +/** + * @override + * @return {string} The tweaks's value. + */ +goog.tweak.StringSetting.prototype.getValue; + + +/** + * @override + * @return {string} The tweaks's new value. + */ +goog.tweak.StringSetting.prototype.getNewValue; + + +/** + * @override + * @param {string} value The tweaks's value. + */ +goog.tweak.StringSetting.prototype.setValue; + + +/** + * @override + * @param {string} value The default value. + */ +goog.tweak.StringSetting.prototype.setDefaultValue; + + +/** + * @override + * @return {string} The default value. + */ +goog.tweak.StringSetting.prototype.getDefaultValue; + + +/** + * @override + */ +goog.tweak.StringSetting.prototype.encodeNewValue = function() { + return this.getNewValue(); +}; + + +/** + * Sets the valid values for the setting. + * @param {Array|undefined} values Valid values. + */ +goog.tweak.StringSetting.prototype.setValidValues = function(values) { + this.assertNotInitialized('setValidValues'); + this.validValues_ = values; + // Set the default value to the first value in the list if the current + // default value is not within it. + if (values && !goog.array.contains(values, this.getDefaultValue())) { + this.setDefaultValue(values[0]); + } +}; + + +/** + * Returns the valid values for the setting. + * @return {Array|undefined} Valid values. + */ +goog.tweak.StringSetting.prototype.getValidValues = function() { + return this.validValues_; +}; + + +/** + * @override + */ +goog.tweak.StringSetting.prototype.initialize = function(value) { + if (value == null) { + this.setValue(this.getDefaultValue()); + } else { + var validValues = this.validValues_; + if (validValues) { + // Make the query parameter values case-insensitive since users might + // type them by hand. Make the capitalization that is actual used come + // from the list of valid values. + value = value.toLowerCase(); + for (var i = 0, il = validValues.length; i < il; ++i) { + if (value == validValues[i].toLowerCase()) { + this.setValue(validValues[i]); + return; + } + } + // Warn if the value is not in the list of allowed values. + goog.log.warning( + this.logger, 'Tweak ' + this.getId() + + ' has value outside of expected range:' + value); + } + this.setValue(value); + } +}; + + + +/** + * A registry setting for numeric values. + * @param {string} id The ID for the setting. + * @param {string} description A description of what the setting does. + * @constructor + * @extends {goog.tweak.BasePrimitiveSetting} + * @final + */ +goog.tweak.NumericSetting = function(id, description) { + goog.tweak.BasePrimitiveSetting.call(this, id, description, 0); + /** + * Valid values for the setting. + * @type {Array|undefined} + */ + this.validValues_; +}; +goog.inherits(goog.tweak.NumericSetting, goog.tweak.BasePrimitiveSetting); + + +/** + * The logger for this class. + * @type {goog.log.Logger} + * @protected + * @override + */ +goog.tweak.NumericSetting.prototype.logger = + goog.log.getLogger('goog.tweak.NumericSetting'); + + +/** + * @override + * @return {number} The tweaks's value. + */ +goog.tweak.NumericSetting.prototype.getValue; + + +/** + * @override + * @return {number} The tweaks's new value. + */ +goog.tweak.NumericSetting.prototype.getNewValue; + + +/** + * @override + * @param {number} value The tweaks's value. + */ +goog.tweak.NumericSetting.prototype.setValue; + + +/** + * @override + * @param {number} value The default value. + */ +goog.tweak.NumericSetting.prototype.setDefaultValue; + + +/** + * @override + * @return {number} The default value. + */ +goog.tweak.NumericSetting.prototype.getDefaultValue; + + +/** + * @override + */ +goog.tweak.NumericSetting.prototype.encodeNewValue = function() { + return '' + this.getNewValue(); +}; + + +/** + * Sets the valid values for the setting. + * @param {Array|undefined} values Valid values. + */ +goog.tweak.NumericSetting.prototype.setValidValues = function(values) { + this.assertNotInitialized('setValidValues'); + this.validValues_ = values; + // Set the default value to the first value in the list if the current + // default value is not within it. + if (values && !goog.array.contains(values, this.getDefaultValue())) { + this.setDefaultValue(values[0]); + } +}; + + +/** + * Returns the valid values for the setting. + * @return {Array|undefined} Valid values. + */ +goog.tweak.NumericSetting.prototype.getValidValues = function() { + return this.validValues_; +}; + + +/** + * @override + */ +goog.tweak.NumericSetting.prototype.initialize = function(value) { + if (value == null) { + this.setValue(this.getDefaultValue()); + } else { + var coercedValue = +value; + // Warn if the value is not in the list of allowed values. + if (this.validValues_ && + !goog.array.contains(this.validValues_, coercedValue)) { + goog.log.warning( + this.logger, 'Tweak ' + this.getId() + + ' has value outside of expected range: ' + value); + } + + if (isNaN(coercedValue)) { + goog.log.warning( + this.logger, 'Tweak ' + this.getId() + + ' has value of NaN, resetting to ' + this.getDefaultValue()); + this.setValue(this.getDefaultValue()); + } else { + this.setValue(coercedValue); + } + } +}; + + + +/** + * A registry setting that can be either true of false. + * @param {string} id The ID for the setting. + * @param {string} description A description of what the setting does. + * @constructor + * @extends {goog.tweak.BasePrimitiveSetting} + */ +goog.tweak.BooleanSetting = function(id, description) { + goog.tweak.BasePrimitiveSetting.call(this, id, description, false); +}; +goog.inherits(goog.tweak.BooleanSetting, goog.tweak.BasePrimitiveSetting); + + +/** + * The logger for this class. + * @type {goog.log.Logger} + * @protected + * @override + */ +goog.tweak.BooleanSetting.prototype.logger = + goog.log.getLogger('goog.tweak.BooleanSetting'); + + +/** + * @override + * @return {boolean} The tweaks's value. + */ +goog.tweak.BooleanSetting.prototype.getValue; + + +/** + * @override + * @return {boolean} The tweaks's new value. + */ +goog.tweak.BooleanSetting.prototype.getNewValue; + + +/** + * @override + * @param {boolean} value The tweaks's value. + */ +goog.tweak.BooleanSetting.prototype.setValue; + + +/** + * @override + * @param {boolean} value The default value. + */ +goog.tweak.BooleanSetting.prototype.setDefaultValue; + + +/** + * @override + * @return {boolean} The default value. + */ +goog.tweak.BooleanSetting.prototype.getDefaultValue; + + +/** + * @override + */ +goog.tweak.BooleanSetting.prototype.encodeNewValue = function() { + return this.getNewValue() ? '1' : '0'; +}; + + +/** + * @override + */ +goog.tweak.BooleanSetting.prototype.initialize = function(value) { + if (value == null) { + this.setValue(this.getDefaultValue()); + } else { + value = value.toLowerCase(); + this.setValue(value == 'true' || value == '1'); + } +}; + + + +/** + * An entry in a BooleanGroup. + * @param {string} id The ID for the setting. + * @param {string} description A description of what the setting does. + * @param {!goog.tweak.BooleanGroup} group The group that this entry belongs + * to. + * @constructor + * @extends {goog.tweak.BooleanSetting} + * @final + */ +goog.tweak.BooleanInGroupSetting = function(id, description, group) { + goog.tweak.BooleanSetting.call(this, id, description); + + /** + * The token to use in the query parameter. + * @type {string} + * @private + */ + this.token_ = this.getId().toLowerCase(); + + /** + * The BooleanGroup that this setting belongs to. + * @type {!goog.tweak.BooleanGroup} + * @private + */ + this.group_ = group; + + // Take setting out of top-level query parameter list. + goog.tweak.BooleanInGroupSetting.superClass_.setParamName.call(this, null); +}; +goog.inherits(goog.tweak.BooleanInGroupSetting, goog.tweak.BooleanSetting); + + +/** + * The logger for this class. + * @type {goog.log.Logger} + * @protected + * @override + */ +goog.tweak.BooleanInGroupSetting.prototype.logger = + goog.log.getLogger('goog.tweak.BooleanInGroupSetting'); + + +/** + * @override + */ +goog.tweak.BooleanInGroupSetting.prototype.setParamName = function(value) { + goog.asserts.fail('Use setToken() for BooleanInGroupSetting.'); +}; + + +/** + * Sets the token to use in the query parameter. + * @param {string} value The value. + */ +goog.tweak.BooleanInGroupSetting.prototype.setToken = function(value) { + this.token_ = value; +}; + + +/** + * Returns the token to use in the query parameter. + * @return {string} The value. + */ +goog.tweak.BooleanInGroupSetting.prototype.getToken = function() { + return this.token_; +}; + + +/** + * Returns the BooleanGroup that this setting belongs to. + * @return {!goog.tweak.BooleanGroup} The BooleanGroup that this setting + * belongs to. + */ +goog.tweak.BooleanInGroupSetting.prototype.getGroup = function() { + return this.group_; +}; + + + +/** + * A registry setting that contains a group of boolean subfield, where all + * entries modify the same query parameter. For example: + * ?foo=setting1,-setting2 + * @param {string} id The ID for the setting. + * @param {string} description A description of what the setting does. + * @constructor + * @extends {goog.tweak.BaseSetting} + * @final + */ +goog.tweak.BooleanGroup = function(id, description) { + goog.tweak.BaseSetting.call(this, id, description); + + /** + * A map of token->child entry. + * @type {!Object} + * @private + */ + this.entriesByToken_ = {}; + + + /** + * A map of token->true/false for all tokens that appeared in the query + * parameter. + * @type {!Object} + * @private + */ + this.queryParamValues_ = {}; + +}; +goog.inherits(goog.tweak.BooleanGroup, goog.tweak.BaseSetting); + + +/** + * The logger for this class. + * @type {goog.log.Logger} + * @protected + * @override + */ +goog.tweak.BooleanGroup.prototype.logger = + goog.log.getLogger('goog.tweak.BooleanGroup'); + + +/** + * Returns the map of token->boolean settings. + * @return {!Object} The child settings. + */ +goog.tweak.BooleanGroup.prototype.getChildEntries = function() { + return this.entriesByToken_; +}; + + +/** + * Adds the given BooleanSetting to the group. + * @param {goog.tweak.BooleanInGroupSetting} boolEntry The entry. + */ +goog.tweak.BooleanGroup.prototype.addChild = function(boolEntry) { + this.ensureInitialized(); + + var token = boolEntry.getToken(); + var lcToken = token.toLowerCase(); + goog.asserts.assert( + !this.entriesByToken_[lcToken], + 'Multiple bools registered with token "%s" in group: %s', token, + this.getId()); + this.entriesByToken_[lcToken] = boolEntry; + + // Initialize from query param. + var value = this.queryParamValues_[lcToken]; + if (value != undefined) { + boolEntry.initialQueryParamValue = value ? '1' : '0'; + } +}; + + +/** + * @override + */ +goog.tweak.BooleanGroup.prototype.initialize = function(value) { + var queryParamValues = {}; + + if (value) { + var tokens = value.split(/\s*,\s*/); + for (var i = 0; i < tokens.length; ++i) { + var token = tokens[i].toLowerCase(); + var negative = token.charAt(0) == '-'; + if (negative) { + token = token.substr(1); + } + queryParamValues[token] = !negative; + } + } + this.queryParamValues_ = queryParamValues; +}; + + +/** + * @override + */ +goog.tweak.BooleanGroup.prototype.getNewValueEncoded = function() { + this.ensureInitialized(); + var nonDefaultValues = []; + // Sort the keys so that the generate value is stable. + var keys = goog.object.getKeys(this.entriesByToken_); + keys.sort(); + for (var i = 0, entry; entry = this.entriesByToken_[keys[i]]; ++i) { + var encodedValue = entry.getNewValueEncoded(); + if (encodedValue != null) { + nonDefaultValues.push( + (entry.getNewValue() ? '' : '-') + entry.getToken()); + } + } + return nonDefaultValues.length ? nonDefaultValues.join(',') : null; +}; + + + +/** + * A registry action (a button). + * @param {string} id The ID for the setting. + * @param {string} description A description of what the setting does. + * @param {!Function} callback Function to call when the button is clicked. + * @constructor + * @extends {goog.tweak.BaseEntry} + * @final + */ +goog.tweak.ButtonAction = function(id, description, callback) { + goog.tweak.BaseEntry.call(this, id, description); + this.addCallback(callback); + this.setRestartRequired(false); +}; +goog.inherits(goog.tweak.ButtonAction, goog.tweak.BaseEntry); diff --git a/closure-library/closure/goog/tweak/registry.js b/closure-library/closure/goog/tweak/registry.js new file mode 100644 index 0000000000..dae2bb9f79 --- /dev/null +++ b/closure-library/closure/goog/tweak/registry.js @@ -0,0 +1,322 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Definition for goog.tweak.Registry. + * Most clients should not use this class directly, but instead use the API + * defined in tweak.js. One possible use case for directly using TweakRegistry + * is to register tweaks that are not known at compile time. + * + * @author agrieve@google.com (Andrew Grieve) + */ + +goog.provide('goog.tweak.Registry'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.log'); +goog.require('goog.string'); +goog.require('goog.tweak.BasePrimitiveSetting'); +goog.require('goog.tweak.BaseSetting'); +goog.require('goog.tweak.BooleanSetting'); +goog.require('goog.tweak.NumericSetting'); +goog.require('goog.tweak.StringSetting'); +goog.require('goog.uri.utils'); + + + +/** + * Singleton that manages all tweaks. This should be instantiated only from + * goog.tweak.getRegistry(). + * @param {string} queryParams Value of window.location.search. + * @param {!Object} compilerOverrides Default value + * overrides set by the compiler. + * @constructor + * @final + */ +goog.tweak.Registry = function(queryParams, compilerOverrides) { + /** + * A map of entry id -> entry object + * @type {!Object} + * @private + */ + this.entryMap_ = {}; + + /** + * The map of query params to use when initializing entry settings. + * @type {!Object} + * @private + */ + this.parsedQueryParams_ = goog.tweak.Registry.parseQueryParams(queryParams); + + /** + * List of callbacks to call when a new entry is registered. + * @type {!Array} + * @private + */ + this.onRegisterListeners_ = []; + + /** + * A map of entry ID -> default value override for overrides set by the + * compiler. + * @type {!Object} + * @private + */ + this.compilerDefaultValueOverrides_ = compilerOverrides; + + /** + * A map of entry ID -> default value override for overrides set by + * goog.tweak.overrideDefaultValue(). + * @type {!Object} + * @private + */ + this.defaultValueOverrides_ = {}; +}; + + +/** + * The logger for this class. + * @type {goog.log.Logger} + * @private + */ +goog.tweak.Registry.prototype.logger_ = + goog.log.getLogger('goog.tweak.Registry'); + + +/** + * Simple parser for query params. Makes all keys lower-case. + * @param {string} queryParams The part of the url between the ? and the #. + * @return {!Object} map of key->value. + */ +goog.tweak.Registry.parseQueryParams = function(queryParams) { + // Strip off the leading ? and split on &. + var parts = queryParams.substr(1).split('&'); + var ret = {}; + + for (var i = 0, il = parts.length; i < il; ++i) { + var entry = parts[i].split('='); + if (entry[0]) { + ret[goog.string.urlDecode(entry[0]).toLowerCase()] = + goog.string.urlDecode(entry[1] || ''); + } + } + return ret; +}; + + +/** + * Registers the given tweak setting/action. + * @param {goog.tweak.BaseEntry} entry The entry. + */ +goog.tweak.Registry.prototype.register = function(entry) { + var id = entry.getId(); + var oldBaseEntry = this.entryMap_[id]; + if (oldBaseEntry) { + if (oldBaseEntry == entry) { + goog.log.warning(this.logger_, 'Tweak entry registered twice: ' + id); + return; + } + goog.asserts.fail( + 'Tweak entry registered twice and with different types: ' + id); + } + + // Check for a default value override, either from compiler flags or from a + // call to overrideDefaultValue(). + var defaultValueOverride = (id in this.compilerDefaultValueOverrides_) ? + this.compilerDefaultValueOverrides_[id] : + this.defaultValueOverrides_[id]; + if (goog.isDef(defaultValueOverride)) { + goog.asserts.assertInstanceof( + entry, goog.tweak.BasePrimitiveSetting, + 'Cannot set the default value of non-primitive setting %s', + entry.label); + entry.setDefaultValue(defaultValueOverride); + } + + // Set its value from the query params. + if (entry instanceof goog.tweak.BaseSetting) { + if (entry.getParamName()) { + entry.setInitialQueryParamValue( + this.parsedQueryParams_[entry.getParamName()]); + } + } + + this.entryMap_[id] = entry; + // Call all listeners. + for (var i = 0, callback; callback = this.onRegisterListeners_[i]; ++i) { + callback(entry); + } +}; + + +/** + * Adds a callback to be called whenever a new tweak is added. + * @param {!Function} func The callback. + */ +goog.tweak.Registry.prototype.addOnRegisterListener = function(func) { + this.onRegisterListeners_.push(func); +}; + + +/** + * @param {string} id The unique string that identifies this entry. + * @return {boolean} Whether a tweak with the given ID is registered. + */ +goog.tweak.Registry.prototype.hasEntry = function(id) { + return id in this.entryMap_; +}; + + +/** + * Returns the BaseEntry with the given ID. Asserts if it does not exists. + * @param {string} id The unique string that identifies this entry. + * @return {!goog.tweak.BaseEntry} The entry. + */ +goog.tweak.Registry.prototype.getEntry = function(id) { + var ret = this.entryMap_[id]; + goog.asserts.assert(ret, 'Tweak not registered: %s', id); + return ret; +}; + + +/** + * Returns the boolean setting with the given ID. Asserts if the ID does not + * refer to a registered entry or if it refers to one of the wrong type. + * @param {string} id The unique string that identifies this entry. + * @return {!goog.tweak.BooleanSetting} The entry. + */ +goog.tweak.Registry.prototype.getBooleanSetting = function(id) { + var entry = this.getEntry(id); + goog.asserts.assertInstanceof( + entry, goog.tweak.BooleanSetting, + 'getBooleanSetting called on wrong type of BaseSetting'); + return /** @type {!goog.tweak.BooleanSetting} */ (entry); +}; + + +/** + * Returns the string setting with the given ID. Asserts if the ID does not + * refer to a registered entry or if it refers to one of the wrong type. + * @param {string} id The unique string that identifies this entry. + * @return {!goog.tweak.StringSetting} The entry. + */ +goog.tweak.Registry.prototype.getStringSetting = function(id) { + var entry = this.getEntry(id); + goog.asserts.assertInstanceof( + entry, goog.tweak.StringSetting, + 'getStringSetting called on wrong type of BaseSetting'); + return /** @type {!goog.tweak.StringSetting} */ (entry); +}; + + +/** + * Returns the numeric setting with the given ID. Asserts if the ID does not + * refer to a registered entry or if it refers to one of the wrong type. + * @param {string} id The unique string that identifies this entry. + * @return {!goog.tweak.NumericSetting} The entry. + */ +goog.tweak.Registry.prototype.getNumericSetting = function(id) { + var entry = this.getEntry(id); + goog.asserts.assertInstanceof( + entry, goog.tweak.NumericSetting, + 'getNumericSetting called on wrong type of BaseSetting'); + return /** @type {!goog.tweak.NumericSetting} */ (entry); +}; + + +/** + * Creates and returns an array of all BaseSetting objects with an associted + * query parameter. + * @param {boolean} excludeChildEntries Exclude BooleanInGroupSettings. + * @param {boolean} excludeNonSettings Exclude entries that are not subclasses + * of BaseSetting. + * @return {!Array} The settings. + */ +goog.tweak.Registry.prototype.extractEntries = function( + excludeChildEntries, excludeNonSettings) { + var entries = []; + for (var id in this.entryMap_) { + var entry = this.entryMap_[id]; + if (entry instanceof goog.tweak.BaseSetting) { + if (excludeChildEntries && !entry.getParamName()) { + continue; + } + } else if (excludeNonSettings) { + continue; + } + entries.push(entry); + } + return entries; +}; + + +/** + * Returns the query part of the URL that will apply all set tweaks. + * @param {string=} opt_existingSearchStr The part of the url between the ? and + * the #. Uses window.location.search if not given. + * @return {string} The query string. + */ +goog.tweak.Registry.prototype.makeUrlQuery = function(opt_existingSearchStr) { + var existingParams = opt_existingSearchStr == undefined ? + window.location.search : + opt_existingSearchStr; + + var sortedEntries = this.extractEntries( + true /* excludeChildEntries */, true /* excludeNonSettings */); + // Sort the params so that the urlQuery has stable ordering. + sortedEntries.sort(function(a, b) { + return goog.array.defaultCompare(a.getParamName(), b.getParamName()); + }); + + // Add all values that are not set to their defaults. + var keysAndValues = []; + for (var i = 0, entry; entry = sortedEntries[i]; ++i) { + var encodedValue = entry.getNewValueEncoded(); + if (encodedValue != null) { + keysAndValues.push(entry.getParamName(), encodedValue); + } + // Strip all tweak query params from the existing query string. This will + // make the final query string contain only the tweak settings that are set + // to their non-default values and also maintain non-tweak related query + // parameters. + existingParams = goog.uri.utils.removeParam( + existingParams, + encodeURIComponent(/** @type {string} */ (entry.getParamName()))); + } + + var tweakParams = goog.uri.utils.buildQueryData(keysAndValues); + // Decode spaces and commas in order to make the URL more readable. + tweakParams = tweakParams.replace(/%2C/g, ',').replace(/%20/g, '+'); + return !tweakParams ? existingParams : existingParams ? + existingParams + '&' + tweakParams : + '?' + tweakParams; +}; + + +/** + * Sets a default value to use for the given tweak instead of the one passed + * to the register* function. This function must be called before the tweak is + * registered. + * @param {string} id The unique string that identifies the entry. + * @param {string|number|boolean} value The replacement value to be used as the + * default value for the setting. + */ +goog.tweak.Registry.prototype.overrideDefaultValue = function(id, value) { + goog.asserts.assert( + !this.hasEntry(id), + 'goog.tweak.overrideDefaultValue must be called before the tweak is ' + + 'registered. Tweak: %s', + id); + this.defaultValueOverrides_[id] = value; +}; diff --git a/closure-library/closure/goog/tweak/testhelpers.js b/closure-library/closure/goog/tweak/testhelpers.js new file mode 100644 index 0000000000..acb7084555 --- /dev/null +++ b/closure-library/closure/goog/tweak/testhelpers.js @@ -0,0 +1,121 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Common test functions for tweak unit tests. + * + * @author agrieve@google.com (Andrew Grieve) + * @package + */ + +goog.provide('goog.tweak.testhelpers'); + +goog.setTestOnly(); + +goog.require('goog.tweak'); +goog.require('goog.tweak.BooleanGroup'); +goog.require('goog.tweak.BooleanInGroupSetting'); +goog.require('goog.tweak.BooleanSetting'); +goog.require('goog.tweak.ButtonAction'); +goog.require('goog.tweak.NumericSetting'); +goog.require('goog.tweak.Registry'); +goog.require('goog.tweak.StringSetting'); + + +var boolEntry; +var boolEntry2; +var strEntry; +var strEntry2; +var strEnumEntry; +var numEntry; +var numEnumEntry; +var boolGroup; +var boolOneEntry; +var boolTwoEntry; +var buttonEntry; + + +/** + * Creates a registry with some entries in it. + * @param {string} queryParams The query parameter string to use for the + * registry. + * @param {!Object=} opt_compilerOverrides Compiler + * overrides. + * @suppress {accessControls} Private state is accessed for test purposes. + */ +function createRegistryEntries(queryParams, opt_compilerOverrides) { + // Initialize the registry with the given query string. + var registry = + new goog.tweak.Registry(queryParams, opt_compilerOverrides || {}); + goog.tweak.registry_ = registry; + + boolEntry = new goog.tweak.BooleanSetting('Bool', 'The bool1'); + registry.register(boolEntry); + + boolEntry2 = new goog.tweak.BooleanSetting('Bool2', 'The bool2'); + boolEntry2.setDefaultValue(true); + registry.register(boolEntry2); + + strEntry = new goog.tweak.StringSetting('Str', 'The str1'); + strEntry.setParamName('s'); + registry.register(strEntry); + + strEntry2 = new goog.tweak.StringSetting('Str2', 'The str2'); + strEntry2.setDefaultValue('foo'); + registry.register(strEntry2); + + strEnumEntry = new goog.tweak.StringSetting('Enum', 'The enum'); + strEnumEntry.setValidValues(['A', 'B', 'C']); + strEnumEntry.setRestartRequired(false); + registry.register(strEnumEntry); + + numEntry = new goog.tweak.NumericSetting('Num', 'The num'); + numEntry.setDefaultValue(99); + registry.register(numEntry); + + numEnumEntry = new goog.tweak.NumericSetting('Enum2', 'The 2nd enum'); + numEnumEntry.setValidValues([1, 2, 3]); + numEnumEntry.setRestartRequired(false); + numEnumEntry.label = 'Enum the second&'; + registry.register(numEnumEntry); + + boolGroup = new goog.tweak.BooleanGroup('BoolGroup', 'The bool group'); + registry.register(boolGroup); + + boolOneEntry = + new goog.tweak.BooleanInGroupSetting('BoolOne', 'Desc for 1', boolGroup); + boolOneEntry.setToken('B1'); + boolOneEntry.setRestartRequired(false); + boolGroup.addChild(boolOneEntry); + registry.register(boolOneEntry); + + boolTwoEntry = + new goog.tweak.BooleanInGroupSetting('BoolTwo', 'Desc for 2', boolGroup); + boolTwoEntry.setDefaultValue(true); + boolGroup.addChild(boolTwoEntry); + registry.register(boolTwoEntry); + + buttonEntry = + new goog.tweak.ButtonAction('Button', 'The Btn', goog.nullFunction); + buttonEntry.label = ''; + registry.register(buttonEntry); + + var nsBoolGroup = + new goog.tweak.BooleanGroup('foo.bar.BoolGroup', 'Namespaced Bool Group'); + registry.register(nsBoolGroup); + var nsBool = new goog.tweak.BooleanInGroupSetting( + 'foo.bar.BoolOne', 'Desc for Namespaced 1', nsBoolGroup); + nsBoolGroup.addChild(nsBool); + registry.register(nsBool); +} diff --git a/closure-library/closure/goog/tweak/tweak.js b/closure-library/closure/goog/tweak/tweak.js new file mode 100644 index 0000000000..8633c4144e --- /dev/null +++ b/closure-library/closure/goog/tweak/tweak.js @@ -0,0 +1,311 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Provides facilities for creating and querying tweaks. + * @see http://code.google.com/p/closure-library/wiki/UsingTweaks + * + * @author agrieve@google.com (Andrew Grieve) + */ + +goog.provide('goog.tweak'); +goog.provide('goog.tweak.ConfigParams'); + +goog.require('goog.asserts'); +goog.require('goog.tweak.BaseSetting'); +goog.require('goog.tweak.BooleanGroup'); +goog.require('goog.tweak.BooleanInGroupSetting'); +goog.require('goog.tweak.BooleanSetting'); +goog.require('goog.tweak.ButtonAction'); +goog.require('goog.tweak.NumericSetting'); +goog.require('goog.tweak.Registry'); +goog.require('goog.tweak.StringSetting'); + + +/** + * Calls to this function are overridden by the compiler by the processTweaks + * pass. It returns the overrides to default values for tweaks set by compiler + * options. + * @return {!Object} A map of tweakId -> defaultValue. + * @private + */ +goog.tweak.getCompilerOverrides_ = function() { + return {}; +}; + + +/** + * The global reference to the registry, if it exists. + * @type {goog.tweak.Registry} + * @private + */ +goog.tweak.registry_ = null; + + +/** + * The boolean group set by beginBooleanGroup and cleared by endBooleanGroup. + * @type {goog.tweak.BooleanGroup} + * @private + */ +goog.tweak.activeBooleanGroup_ = null; + + +/** + * Returns/creates the registry singleton. + * @return {!goog.tweak.Registry} The tweak registry. + */ +goog.tweak.getRegistry = function() { + if (!goog.tweak.registry_) { + var queryString = window.location.search; + var overrides = goog.tweak.getCompilerOverrides_(); + goog.tweak.registry_ = new goog.tweak.Registry(queryString, overrides); + } + return goog.tweak.registry_; +}; + + +/** + * Type for configParams. + * TODO(agrieve): Remove |Object when optional fields in struct types are + * implemented. + * @typedef {{ + * label:(string|undefined), + * validValues:(!Array|!Array|undefined), + * paramName:(string|undefined), + * restartRequired:(boolean|undefined), + * callback:(Function|undefined), + * token:(string|undefined) + * }|!Object} + */ +goog.tweak.ConfigParams; + + +/** + * Applies all extra configuration parameters in configParams. + * @param {!goog.tweak.BaseEntry} entry The entry to apply them to. + * @param {!goog.tweak.ConfigParams} configParams Extra configuration + * parameters. + * @private + */ +goog.tweak.applyConfigParams_ = function(entry, configParams) { + if (configParams.label) { + entry.label = configParams.label; + delete configParams.label; + } + if (configParams.validValues) { + goog.asserts.assert( + entry instanceof goog.tweak.StringSetting || + entry instanceof goog.tweak.NumericSetting, + 'Cannot set validValues on tweak: %s', entry.getId()); + if (entry instanceof goog.tweak.StringSetting) { + entry.setValidValues(configParams.validValues); + } else if (entry instanceof goog.tweak.NumericSetting) { + entry.setValidValues(configParams.validValues); + } + delete configParams.validValues; + } + if (goog.isDef(configParams.paramName)) { + goog.asserts.assertInstanceof( + entry, goog.tweak.BaseSetting, 'Cannot set paramName on tweak: %s', + entry.getId()); + entry.setParamName(configParams.paramName); + delete configParams.paramName; + } + if (goog.isDef(configParams.restartRequired)) { + entry.setRestartRequired(configParams.restartRequired); + delete configParams.restartRequired; + } + if (configParams.callback) { + entry.addCallback(configParams.callback); + delete configParams.callback; + goog.asserts.assert( + !entry.isRestartRequired() || (configParams.restartRequired == false), + 'Tweak %s should set restartRequired: false, when adding a callback.', + entry.getId()); + } + if (configParams.token) { + goog.asserts.assertInstanceof( + entry, goog.tweak.BooleanInGroupSetting, + 'Cannot set token on tweak: %s', entry.getId()); + entry.setToken(configParams.token); + delete configParams.token; + } + for (var key in configParams) { + goog.asserts.fail( + 'Unknown config options (' + key + '=' + configParams[key] + + ') for tweak ' + entry.getId()); + } +}; + + +/** + * Registers a tweak using the given factoryFunc. + * @param {!goog.tweak.BaseEntry} entry The entry to register. + * @param {boolean|string|number=} opt_defaultValue Default value. + * @param {goog.tweak.ConfigParams=} opt_configParams Extra + * configuration parameters. + * @private + */ +goog.tweak.doRegister_ = function(entry, opt_defaultValue, opt_configParams) { + if (opt_configParams) { + goog.tweak.applyConfigParams_(entry, opt_configParams); + } + if (opt_defaultValue != undefined) { + entry.setDefaultValue(opt_defaultValue); + } + if (goog.tweak.activeBooleanGroup_) { + goog.asserts.assertInstanceof( + entry, goog.tweak.BooleanInGroupSetting, + 'Forgot to end Boolean Group: %s', + goog.tweak.activeBooleanGroup_.getId()); + goog.tweak.activeBooleanGroup_.addChild( + /** @type {!goog.tweak.BooleanInGroupSetting} */ (entry)); + } + goog.tweak.getRegistry().register(entry); +}; + + +/** + * Creates and registers a group of BooleanSettings that are all set by a + * single query parameter. A call to goog.tweak.endBooleanGroup() must be used + * to close this group. Only goog.tweak.registerBoolean() calls are allowed with + * the beginBooleanGroup()/endBooleanGroup(). + * @param {string} id The unique ID for the setting. + * @param {string} description A description of what the setting does. + * @param {goog.tweak.ConfigParams=} opt_configParams Extra configuration + * parameters. + */ +goog.tweak.beginBooleanGroup = function(id, description, opt_configParams) { + var entry = new goog.tweak.BooleanGroup(id, description); + goog.tweak.doRegister_(entry, undefined, opt_configParams); + goog.tweak.activeBooleanGroup_ = entry; +}; + + +/** + * Stops adding boolean entries to the active boolean group. + */ +goog.tweak.endBooleanGroup = function() { + goog.tweak.activeBooleanGroup_ = null; +}; + + +/** + * Creates and registers a BooleanSetting. + * @param {string} id The unique ID for the setting. + * @param {string} description A description of what the setting does. + * @param {boolean=} opt_defaultValue The default value for the setting. + * @param {goog.tweak.ConfigParams=} opt_configParams Extra configuration + * parameters. + */ +goog.tweak.registerBoolean = function( + id, description, opt_defaultValue, opt_configParams) { + // TODO(agrieve): There is a bug in the compiler that causes these calls not + // to be stripped without this outer if. Might be Issue #90. + if (goog.tweak.activeBooleanGroup_) { + var entry = new goog.tweak.BooleanInGroupSetting( + id, description, goog.tweak.activeBooleanGroup_); + } else { + entry = new goog.tweak.BooleanSetting(id, description); + } + goog.tweak.doRegister_(entry, opt_defaultValue, opt_configParams); +}; + + +/** + * Creates and registers a StringSetting. + * @param {string} id The unique ID for the setting. + * @param {string} description A description of what the setting does. + * @param {string=} opt_defaultValue The default value for the setting. + * @param {goog.tweak.ConfigParams=} opt_configParams Extra configuration + * parameters. + */ +goog.tweak.registerString = function( + id, description, opt_defaultValue, opt_configParams) { + goog.tweak.doRegister_( + new goog.tweak.StringSetting(id, description), opt_defaultValue, + opt_configParams); +}; + + +/** + * Creates and registers a NumericSetting. + * @param {string} id The unique ID for the setting. + * @param {string} description A description of what the setting does. + * @param {number=} opt_defaultValue The default value for the setting. + * @param {goog.tweak.ConfigParams=} opt_configParams Extra configuration + * parameters. + */ +goog.tweak.registerNumber = function( + id, description, opt_defaultValue, opt_configParams) { + goog.tweak.doRegister_( + new goog.tweak.NumericSetting(id, description), opt_defaultValue, + opt_configParams); +}; + + +/** + * Creates and registers a ButtonAction. + * @param {string} id The unique ID for the setting. + * @param {string} description A description of what the action does. + * @param {!Function} callback Function to call when the button is clicked. + * @param {string=} opt_label The button text (instead of the ID). + */ +goog.tweak.registerButton = function(id, description, callback, opt_label) { + var tweak = new goog.tweak.ButtonAction(id, description, callback); + tweak.label = opt_label || tweak.label; + goog.tweak.doRegister_(tweak); +}; + + +/** + * Sets a default value to use for the given tweak instead of the one passed + * to the register* function. This function must be called before the tweak is + * registered. + * @param {string} id The unique string that identifies the entry. + * @param {string|number|boolean} value The new default value for the tweak. + */ +goog.tweak.overrideDefaultValue = function(id, value) { + goog.tweak.getRegistry().overrideDefaultValue(id, value); +}; + + +/** + * Returns the value of the boolean setting with the given ID. + * @param {string} id The unique string that identifies this entry. + * @return {boolean} The value of the tweak. + */ +goog.tweak.getBoolean = function(id) { + return goog.tweak.getRegistry().getBooleanSetting(id).getValue(); +}; + + +/** + * Returns the value of the string setting with the given ID, + * @param {string} id The unique string that identifies this entry. + * @return {string} The value of the tweak. + */ +goog.tweak.getString = function(id) { + return goog.tweak.getRegistry().getStringSetting(id).getValue(); +}; + + +/** + * Returns the value of the numeric setting with the given ID. + * @param {string} id The unique string that identifies this entry. + * @return {number} The value of the tweak. + */ +goog.tweak.getNumber = function(id) { + return goog.tweak.getRegistry().getNumericSetting(id).getValue(); +}; diff --git a/closure-library/closure/goog/tweak/tweakui.js b/closure-library/closure/goog/tweak/tweakui.js new file mode 100644 index 0000000000..82a8c629a6 --- /dev/null +++ b/closure-library/closure/goog/tweak/tweakui.js @@ -0,0 +1,854 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A UI for editing tweak settings / clicking tweak actions. + * + * @author agrieve@google.com (Andrew Grieve) + */ + +goog.provide('goog.tweak.EntriesPanel'); +goog.provide('goog.tweak.TweakUi'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.safe'); +goog.require('goog.html.SafeHtml'); +goog.require('goog.html.SafeStyleSheet'); +goog.require('goog.object'); +goog.require('goog.string.Const'); +goog.require('goog.style'); +goog.require('goog.tweak'); +goog.require('goog.tweak.BaseEntry'); +goog.require('goog.tweak.BooleanGroup'); +goog.require('goog.tweak.BooleanInGroupSetting'); +goog.require('goog.tweak.BooleanSetting'); +goog.require('goog.tweak.ButtonAction'); +goog.require('goog.tweak.NumericSetting'); +goog.require('goog.tweak.StringSetting'); +goog.require('goog.ui.Zippy'); +goog.require('goog.userAgent'); + + + +/** + * A UI for editing tweak settings / clicking tweak actions. + * @param {!goog.tweak.Registry} registry The registry to render. + * @param {goog.dom.DomHelper=} opt_domHelper The DomHelper to render with. + * @constructor + * @final + */ +goog.tweak.TweakUi = function(registry, opt_domHelper) { + /** + * The registry to create a UI from. + * @type {!goog.tweak.Registry} + * @private + */ + this.registry_ = registry; + + /** + * The element to display when the UI is visible. + * @type {goog.tweak.EntriesPanel|undefined} + * @private + */ + this.entriesPanel_; + + /** + * The DomHelper to render with. + * @type {!goog.dom.DomHelper} + * @private + */ + this.domHelper_ = opt_domHelper || goog.dom.getDomHelper(); + + // Listen for newly registered entries (happens with lazy-loaded modules). + registry.addOnRegisterListener(goog.bind(this.onNewRegisteredEntry_, this)); +}; + + +/** + * The CSS class name unique to the root tweak panel div. + * @type {string} + * @private + */ +goog.tweak.TweakUi.ROOT_PANEL_CLASS_ = goog.getCssName('goog-tweak-root'); + + +/** + * The CSS class name unique to the tweak entry div. + * @type {string} + * @private + */ +goog.tweak.TweakUi.ENTRY_CSS_CLASS_ = goog.getCssName('goog-tweak-entry'); + + +/** + * The CSS classes for each tweak entry div. + * @type {string} + * @private + */ +goog.tweak.TweakUi.ENTRY_CSS_CLASSES_ = goog.tweak.TweakUi.ENTRY_CSS_CLASS_ + + ' ' + goog.getCssName('goog-inline-block'); + + +/** + * The CSS classes for each namespace tweak entry div. + * @type {string} + * @private + */ +goog.tweak.TweakUi.ENTRY_GROUP_CSS_CLASSES_ = + goog.tweak.TweakUi.ENTRY_CSS_CLASS_; + + +/** + * Marker that the style sheet has already been installed. + * @type {string} + * @private + */ +goog.tweak.TweakUi.STYLE_SHEET_INSTALLED_MARKER_ = '__closure_tweak_installed_'; + + +/** + * CSS used by TweakUI. + * @type {!goog.html.SafeStyleSheet} + * @private + */ +goog.tweak.TweakUi.CSS_STYLES_ = (function() { + var MOBILE = goog.userAgent.MOBILE; + var IE = goog.userAgent.IE; + var ROOT_PANEL_CLASS = '.' + goog.tweak.TweakUi.ROOT_PANEL_CLASS_; + var GOOG_INLINE_BLOCK_CLASS = '.' + goog.getCssName('goog-inline-block'); + var ret = [goog.html.SafeStyleSheet.createRule( + ROOT_PANEL_CLASS, {'background': '#ffc', 'padding': '0 4px'})]; + // Make this work even if the user hasn't included common.css. + if (!IE) { + ret.push(goog.html.SafeStyleSheet.createRule( + GOOG_INLINE_BLOCK_CLASS, {'display': 'inline-block'})); + } + // Space things out vertically for touch UIs. + if (MOBILE) { + ret.push(goog.html.SafeStyleSheet.createRule( + ROOT_PANEL_CLASS + ',' + ROOT_PANEL_CLASS + ' fieldset', + {'line-height': '2em'})); + } + return goog.html.SafeStyleSheet.concat(ret); +})(); + + +/** + * Creates a TweakUi if tweaks are enabled. + * @param {goog.dom.DomHelper=} opt_domHelper The DomHelper to render with. + * @return {!Element|undefined} The root UI element or undefined if tweaks are + * not enabled. + */ +goog.tweak.TweakUi.create = function(opt_domHelper) { + var registry = goog.tweak.getRegistry(); + if (registry) { + var ui = new goog.tweak.TweakUi(registry, opt_domHelper); + ui.render(); + return ui.getRootElement(); + } +}; + + +/** + * Creates a TweakUi inside of a show/hide link. + * @param {goog.dom.DomHelper=} opt_domHelper The DomHelper to render with. + * @return {!Element|undefined} The root UI element or undefined if tweaks are + * not enabled. + */ +goog.tweak.TweakUi.createCollapsible = function(opt_domHelper) { + var registry = goog.tweak.getRegistry(); + if (registry) { + var dh = opt_domHelper || goog.dom.getDomHelper(); + + // The following strings are for internal debugging only. No translation + // necessary. Do NOT wrap goog.getMsg() around these strings. + var showLink = + dh.createDom(goog.dom.TagName.A, {href: 'javascript:;'}, 'Show Tweaks'); + var hideLink = + dh.createDom(goog.dom.TagName.A, {href: 'javascript:;'}, 'Hide Tweaks'); + var ret = dh.createDom(goog.dom.TagName.DIV, null, showLink); + + var lazyCreate = function() { + // Lazily render the UI. + var ui = new goog.tweak.TweakUi( + /** @type {!goog.tweak.Registry} */ (registry), dh); + ui.render(); + // Put the hide link on the same line as the "Show Descriptions" link. + // Set the style lazily because we can. + hideLink.style.marginRight = '10px'; + var tweakElem = ui.getRootElement(); + tweakElem.insertBefore(hideLink, tweakElem.firstChild); + ret.appendChild(tweakElem); + return tweakElem; + }; + new goog.ui.Zippy(showLink, lazyCreate, false /* expanded */, hideLink); + return ret; + } +}; + + +/** + * Compares the given entries. Orders alphabetically and groups buttons and + * expandable groups. + * @param {!goog.tweak.BaseEntry} a The first entry to compare. + * @param {!goog.tweak.BaseEntry} b The second entry to compare. + * @return {number} Refer to goog.array.defaultCompare. + * @private + */ +goog.tweak.TweakUi.entryCompare_ = function(a, b) { + return ( + goog.array.defaultCompare( + a instanceof goog.tweak.NamespaceEntry_, + b instanceof goog.tweak.NamespaceEntry_) || + goog.array.defaultCompare( + a instanceof goog.tweak.BooleanGroup, + b instanceof goog.tweak.BooleanGroup) || + goog.array.defaultCompare( + a instanceof goog.tweak.ButtonAction, + b instanceof goog.tweak.ButtonAction) || + goog.array.defaultCompare(a.label, b.label) || + goog.array.defaultCompare(a.getId(), b.getId())); +}; + + +/** + * @param {!goog.tweak.BaseEntry} entry The entry. + * @return {boolean} Returns whether the given entry contains sub-entries. + * @private + */ +goog.tweak.TweakUi.isGroupEntry_ = function(entry) { + return entry instanceof goog.tweak.NamespaceEntry_ || + entry instanceof goog.tweak.BooleanGroup; +}; + + +/** + * Returns the list of entries from the given boolean group. + * @param {!goog.tweak.BooleanGroup} group The group to get the entries from. + * @return {!Array} The sorted entries. + * @private + */ +goog.tweak.TweakUi.extractBooleanGroupEntries_ = function(group) { + var ret = goog.object.getValues(group.getChildEntries()); + ret.sort(goog.tweak.TweakUi.entryCompare_); + return ret; +}; + + +/** + * @param {!goog.tweak.BaseEntry} entry The entry. + * @return {string} Returns the namespace for the entry, or '' if it is not + * namespaced. + * @private + */ +goog.tweak.TweakUi.extractNamespace_ = function(entry) { + var namespaceMatch = /.+(?=\.)/.exec(entry.getId()); + return namespaceMatch ? namespaceMatch[0] : ''; +}; + + +/** + * @param {!goog.tweak.BaseEntry} entry The entry. + * @return {string} Returns the part of the label after the last period, unless + * the label has been explicly set (it is different from the ID). + * @private + */ +goog.tweak.TweakUi.getNamespacedLabel_ = function(entry) { + var label = entry.label; + if (label == entry.getId()) { + label = label.substr(label.lastIndexOf('.') + 1); + } + return label; +}; + + +/** + * @return {!Element} The root element. Must not be called before render(). + */ +goog.tweak.TweakUi.prototype.getRootElement = function() { + goog.asserts.assert( + this.entriesPanel_, 'TweakUi.getRootElement called before render().'); + return this.entriesPanel_.getRootElement(); +}; + + +/** + * Reloads the page with query parameters set by the UI. + * @private + */ +goog.tweak.TweakUi.prototype.restartWithAppliedTweaks_ = function() { + var queryString = this.registry_.makeUrlQuery(); + var wnd = this.domHelper_.getWindow(); + if (queryString != wnd.location.search) { + wnd.location.search = queryString; + } else { + wnd.location.reload(); + } +}; + + +/** + * Installs the required CSS styles. + * @private + */ +goog.tweak.TweakUi.prototype.installStyles_ = function() { + // Use an marker to install the styles only once per document. + // Styles are injected via JS instead of in a separate style sheet so that + // they are automatically excluded when tweaks are stripped out. + var doc = this.domHelper_.getDocument(); + if (!(goog.tweak.TweakUi.STYLE_SHEET_INSTALLED_MARKER_ in doc)) { + goog.style.installSafeStyleSheet(goog.tweak.TweakUi.CSS_STYLES_, doc); + doc[goog.tweak.TweakUi.STYLE_SHEET_INSTALLED_MARKER_] = true; + } +}; + + +/** + * Creates the element to display when the UI is visible. + * @return {!Element} The root element. + */ +goog.tweak.TweakUi.prototype.render = function() { + this.installStyles_(); + var dh = this.domHelper_; + // The submit button + var submitButton = dh.createDom( + goog.dom.TagName.BUTTON, {style: 'font-weight:bold'}, 'Apply Tweaks'); + submitButton.onclick = goog.bind(this.restartWithAppliedTweaks_, this); + + var rootPanel = new goog.tweak.EntriesPanel([], dh); + var rootPanelDiv = rootPanel.render(submitButton); + rootPanelDiv.className += ' ' + goog.tweak.TweakUi.ROOT_PANEL_CLASS_; + this.entriesPanel_ = rootPanel; + + var entries = this.registry_.extractEntries( + true /* excludeChildEntries */, false /* excludeNonSettings */); + for (var i = 0, entry; entry = entries[i]; i++) { + this.insertEntry_(entry); + } + + return rootPanelDiv; +}; + + +/** + * Updates the UI with the given entry. + * @param {!goog.tweak.BaseEntry} entry The newly registered entry. + * @private + */ +goog.tweak.TweakUi.prototype.onNewRegisteredEntry_ = function(entry) { + if (this.entriesPanel_) { + this.insertEntry_(entry); + } +}; + + +/** + * Updates the UI with the given entry. + * @param {!goog.tweak.BaseEntry} entry The newly registered entry. + * @private + */ +goog.tweak.TweakUi.prototype.insertEntry_ = function(entry) { + var panel = this.entriesPanel_; + var namespace = goog.tweak.TweakUi.extractNamespace_(entry); + + if (namespace) { + // Find the NamespaceEntry that the entry belongs to. + var namespaceEntryId = goog.tweak.NamespaceEntry_.ID_PREFIX + namespace; + var nsPanel = panel.childPanels[namespaceEntryId]; + if (nsPanel) { + panel = nsPanel; + } else { + entry = new goog.tweak.NamespaceEntry_(namespace, [entry]); + } + } + if (entry instanceof goog.tweak.BooleanInGroupSetting) { + var group = entry.getGroup(); + // BooleanGroup entries are always registered before their + // BooleanInGroupSettings. + panel = panel.childPanels[group.getId()]; + } + goog.asserts.assert(panel, 'Missing panel for entry %s', entry.getId()); + panel.insertEntry(entry); +}; + + + +/** + * The body of the tweaks UI and also used for BooleanGroup. + * @param {!Array} entries The entries to show in the + * panel. + * @param {goog.dom.DomHelper=} opt_domHelper The DomHelper to render with. + * @constructor + * @final + */ +goog.tweak.EntriesPanel = function(entries, opt_domHelper) { + /** + * The entries to show in the panel. + * @type {!Array} entries + * @private + */ + this.entries_ = entries; + + var self = this; + /** + * The bound onclick handler for the help question marks. + * @this {Element} + * @private + */ + this.boundHelpOnClickHandler_ = function() { + self.onHelpClick_(this.parentNode); + }; + + /** + * The element that contains the UI. + * @type {Element} + * @private + */ + this.rootElem_; + + /** + * The element that contains all of the settings and the endElement. + * @type {Element} + * @private + */ + this.mainPanel_; + + /** + * Flips between true/false each time the "Toggle Descriptions" link is + * clicked. + * @type {boolean} + * @private + */ + this.showAllDescriptionsState_; + + /** + * The DomHelper to render with. + * @type {!goog.dom.DomHelper} + * @private + */ + this.domHelper_ = opt_domHelper || goog.dom.getDomHelper(); + + /** + * Map of tweak ID -> EntriesPanel for child panels (BooleanGroups). + * @type {!Object} + */ + this.childPanels = {}; +}; + + +/** + * @return {!Element} Returns the expanded element. Must not be called before + * render(). + */ +goog.tweak.EntriesPanel.prototype.getRootElement = function() { + goog.asserts.assert( + this.rootElem_, 'EntriesPanel.getRootElement called before render().'); + return /** @type {!Element} */ (this.rootElem_); +}; + + +/** + * Creates and returns the expanded element. + * The markup looks like: + * + *

      + * Show Descriptions + *
      + * ... + * {endElement} + *
      + *
      + * + * @param {Element|DocumentFragment=} opt_endElement Element to insert after all + * tweak entries. + * @return {!Element} The root element for the panel. + */ +goog.tweak.EntriesPanel.prototype.render = function(opt_endElement) { + var dh = this.domHelper_; + var entries = this.entries_; + var ret = dh.createDom(goog.dom.TagName.DIV); + + var showAllDescriptionsLink = dh.createDom( + goog.dom.TagName.A, { + href: 'javascript:;', + onclick: goog.bind(this.toggleAllDescriptions, this) + }, + 'Toggle all Descriptions'); + ret.appendChild(showAllDescriptionsLink); + + // Add all of the entries. + var mainPanel = dh.createElement(goog.dom.TagName.DIV); + this.mainPanel_ = mainPanel; + for (var i = 0, entry; entry = entries[i]; i++) { + mainPanel.appendChild(this.createEntryElem_(entry)); + } + + if (opt_endElement) { + mainPanel.appendChild(opt_endElement); + } + ret.appendChild(mainPanel); + this.rootElem_ = ret; + return /** @type {!Element} */ (ret); +}; + + +/** + * Inserts the given entry into the panel. + * @param {!goog.tweak.BaseEntry} entry The entry to insert. + */ +goog.tweak.EntriesPanel.prototype.insertEntry = function(entry) { + var insertIndex = + -goog.array.binarySearch( + this.entries_, entry, goog.tweak.TweakUi.entryCompare_) - + 1; + goog.asserts.assert( + insertIndex >= 0, 'insertEntry failed for %s', entry.getId()); + goog.array.insertAt(this.entries_, entry, insertIndex); + this.mainPanel_.insertBefore( + this.createEntryElem_(entry), + // IE doesn't like 'undefined' here. + this.mainPanel_.childNodes[insertIndex] || null); +}; + + +/** + * Creates and returns a form element for the given entry. + * @param {!goog.tweak.BaseEntry} entry The entry. + * @return {!Element} The root DOM element for the entry. + * @private + */ +goog.tweak.EntriesPanel.prototype.createEntryElem_ = function(entry) { + var dh = this.domHelper_; + var isGroupEntry = goog.tweak.TweakUi.isGroupEntry_(entry); + var classes = isGroupEntry ? goog.tweak.TweakUi.ENTRY_GROUP_CSS_CLASSES_ : + goog.tweak.TweakUi.ENTRY_CSS_CLASSES_; + // Containers should not use label tags or else all descendent inputs will be + // connected on desktop browsers. + var containerNodeName = + isGroupEntry ? goog.dom.TagName.SPAN : goog.dom.TagName.LABEL; + var ret = dh.createDom( + goog.dom.TagName.DIV, classes, + dh.createDom( + containerNodeName, { + // Make the hover text the description. + title: entry.description, + style: 'color:' + (entry.isRestartRequired() ? '' : 'blue') + }, + this.createTweakEntryDom_(entry)), + // Add the expandable help question mark. + this.createHelpElem_(entry)); + return ret; +}; + + +/** + * Click handler for the help link. + * @param {Node} entryDiv The div that contains the tweak. + * @private + */ +goog.tweak.EntriesPanel.prototype.onHelpClick_ = function(entryDiv) { + this.showDescription_(entryDiv, !entryDiv.style.display); +}; + + +/** + * Twiddle the DOM so that the entry within the given span is shown/hidden. + * @param {Node} entryDiv The div that contains the tweak. + * @param {boolean} show True to show, false to hide. + * @private + */ +goog.tweak.EntriesPanel.prototype.showDescription_ = function(entryDiv, show) { + var descriptionElem = entryDiv.lastChild.lastChild; + goog.style.setElementShown(/** @type {Element} */ (descriptionElem), show); + entryDiv.style.display = show ? 'block' : ''; +}; + + +/** + * Creates and returns a help element for the given entry. + * @param {goog.tweak.BaseEntry} entry The entry. + * @return {!Element} The root element of the created DOM. + * @private + */ +goog.tweak.EntriesPanel.prototype.createHelpElem_ = function(entry) { + // The markup looks like: + // ?{description} + var ret = this.domHelper_.createElement(goog.dom.TagName.SPAN); + goog.dom.safe.setInnerHtml( + ret, + goog.html.SafeHtml.concat( + goog.html.SafeHtml.create( + 'b', {'style': goog.string.Const.from('padding:0 1em 0 .5em')}, + '?'), + goog.html.SafeHtml.create( + 'span', + {'style': goog.string.Const.from('display:none;color:#666')}))); + ret.onclick = this.boundHelpOnClickHandler_; + // IE<9 doesn't support lastElementChild. + var descriptionElem = /** @type {!Element} */ (ret.lastChild); + if (entry.isRestartRequired()) { + goog.dom.setTextContent(descriptionElem, entry.description); + } else { + goog.dom.safe.setInnerHtml( + descriptionElem, + goog.html.SafeHtml.concat( + goog.html.SafeHtml.htmlEscape(entry.description), + goog.html.SafeHtml.create( + 'span', {'style': goog.string.Const.from('color: blue')}, + '(no restart required)'))); + } + return ret; +}; + + +/** + * Show all entry descriptions (has the same effect as clicking on all ?'s). + */ +goog.tweak.EntriesPanel.prototype.toggleAllDescriptions = function() { + var show = !this.showAllDescriptionsState_; + this.showAllDescriptionsState_ = show; + var entryDivs = this.domHelper_.getElementsByTagNameAndClass( + goog.dom.TagName.DIV, goog.tweak.TweakUi.ENTRY_CSS_CLASS_, + this.rootElem_); + for (var i = 0, div; div = entryDivs[i]; i++) { + this.showDescription_(div, show); + } +}; + + +/** + * Creates the DOM element to control the given enum setting. + * @param {!goog.tweak.StringSetting|!goog.tweak.NumericSetting} tweak The + * setting. + * @param {string} label The label for the entry. + * @param {!Function} onchangeFunc onchange event handler. + * @return {!DocumentFragment} The DOM element. + * @private + */ +goog.tweak.EntriesPanel.prototype.createComboBoxDom_ = function( + tweak, label, onchangeFunc) { + // The markup looks like: + // Label: + var dh = this.domHelper_; + var ret = dh.getDocument().createDocumentFragment(); + ret.appendChild(dh.createTextNode(label + ': ')); + var selectElem = dh.createElement(goog.dom.TagName.SELECT); + var values = tweak.getValidValues(); + for (var i = 0, il = values.length; i < il; ++i) { + var optionElem = dh.createElement(goog.dom.TagName.OPTION); + optionElem.text = String(values[i]); + // Setting the option tag's value is required for selectElem.value to work + // properly. + optionElem.value = String(values[i]); + selectElem.appendChild(optionElem); + } + ret.appendChild(selectElem); + + // Set the value and add a callback. + selectElem.value = String(tweak.getNewValue()); + selectElem.onchange = onchangeFunc; + tweak.addCallback(function() { + selectElem.value = String(tweak.getNewValue()); + }); + return ret; +}; + + +/** + * Creates the DOM element to control the given boolean setting. + * @param {!goog.tweak.BooleanSetting} tweak The setting. + * @param {string} label The label for the entry. + * @return {!DocumentFragment} The DOM elements. + * @private + */ +goog.tweak.EntriesPanel.prototype.createBooleanSettingDom_ = function( + tweak, label) { + var dh = this.domHelper_; + var ret = dh.getDocument().createDocumentFragment(); + var checkbox = dh.createDom(goog.dom.TagName.INPUT, {type: 'checkbox'}); + ret.appendChild(checkbox); + ret.appendChild(dh.createTextNode(label)); + + // Needed on IE6 to ensure the textbox doesn't get cleared + // when added to the DOM. + checkbox.defaultChecked = tweak.getNewValue(); + + checkbox.checked = tweak.getNewValue(); + checkbox.onchange = function() { tweak.setValue(checkbox.checked); }; + tweak.addCallback(function() { checkbox.checked = tweak.getNewValue(); }); + return ret; +}; + + +/** + * Creates the DOM for a BooleanGroup or NamespaceEntry. + * @param {!goog.tweak.BooleanGroup|!goog.tweak.NamespaceEntry_} entry The + * entry. + * @param {string} label The label for the entry. + * @param {!Array} childEntries The child entries. + * @return {!DocumentFragment} The DOM element. + * @private + */ +goog.tweak.EntriesPanel.prototype.createSubPanelDom_ = function( + entry, label, childEntries) { + var dh = this.domHelper_; + var toggleLink = + dh.createDom(goog.dom.TagName.A, {href: 'javascript:;'}, label + ' \xBB'); + var toggleLink2 = + dh.createDom(goog.dom.TagName.A, {href: 'javascript:;'}, '\xAB ' + label); + toggleLink2.style.marginRight = '10px'; + + var innerUi = new goog.tweak.EntriesPanel(childEntries, dh); + this.childPanels[entry.getId()] = innerUi; + + var elem = innerUi.render(); + // Move the toggle descriptions link into the legend. + var descriptionsLink = elem.firstChild; + var childrenElem = dh.createDom( + goog.dom.TagName.FIELDSET, goog.getCssName('goog-inline-block'), + dh.createDom( + goog.dom.TagName.LEGEND, null, toggleLink2, descriptionsLink), + elem); + + new goog.ui.Zippy( + toggleLink, childrenElem, false /* expanded */, toggleLink2); + + var ret = dh.getDocument().createDocumentFragment(); + ret.appendChild(toggleLink); + ret.appendChild(childrenElem); + return ret; +}; + + +/** + * Creates the DOM element to control the given string setting. + * @param {!goog.tweak.StringSetting|!goog.tweak.NumericSetting} tweak The + * setting. + * @param {string} label The label for the entry. + * @param {!Function} onchangeFunc onchange event handler. + * @return {!DocumentFragment} The DOM element. + * @private + */ +goog.tweak.EntriesPanel.prototype.createTextBoxDom_ = function( + tweak, label, onchangeFunc) { + var dh = this.domHelper_; + var ret = dh.getDocument().createDocumentFragment(); + ret.appendChild(dh.createTextNode(label + ': ')); + var textBox = dh.createDom(goog.dom.TagName.INPUT, { + value: String(tweak.getNewValue()), + // TODO(agrieve): Make size configurable or autogrow. + size: 5, + onblur: onchangeFunc + }); + ret.appendChild(textBox); + tweak.addCallback(function() { + textBox.value = String(tweak.getNewValue()); + }); + return ret; +}; + + +/** + * Creates the DOM element to control the given button action. + * @param {!goog.tweak.ButtonAction} tweak The action. + * @param {string} label The label for the entry. + * @return {!Element} The DOM element. + * @private + */ +goog.tweak.EntriesPanel.prototype.createButtonActionDom_ = function( + tweak, label) { + return this.domHelper_.createDom( + goog.dom.TagName.BUTTON, {onclick: goog.bind(tweak.fireCallbacks, tweak)}, + label); +}; + + +/** + * Creates the DOM element to control the given entry. + * @param {!goog.tweak.BaseEntry} entry The entry. + * @return {!Element|!DocumentFragment} The DOM element. + * @private + */ +goog.tweak.EntriesPanel.prototype.createTweakEntryDom_ = function(entry) { + var label = goog.tweak.TweakUi.getNamespacedLabel_(entry); + if (entry instanceof goog.tweak.BooleanSetting) { + return this.createBooleanSettingDom_(entry, label); + } else if (entry instanceof goog.tweak.BooleanGroup) { + var childEntries = goog.tweak.TweakUi.extractBooleanGroupEntries_(entry); + return this.createSubPanelDom_(entry, label, childEntries); + } else if (entry instanceof goog.tweak.StringSetting) { + /** @this {Element} */ + var setValueFunc = function() { entry.setValue(this.value); }; + return entry.getValidValues() ? + this.createComboBoxDom_(entry, label, setValueFunc) : + this.createTextBoxDom_(entry, label, setValueFunc); + } else if (entry instanceof goog.tweak.NumericSetting) { + /** @this {Element} */ + setValueFunc = function() { + // Reset the value if it's not a number. + if (isNaN(this.value)) { + this.value = entry.getNewValue(); + } else { + entry.setValue(+this.value); + } + }; + return entry.getValidValues() ? + this.createComboBoxDom_(entry, label, setValueFunc) : + this.createTextBoxDom_(entry, label, setValueFunc); + } else if (entry instanceof goog.tweak.NamespaceEntry_) { + return this.createSubPanelDom_(entry, entry.label, entry.entries); + } + goog.asserts.assertInstanceof( + entry, goog.tweak.ButtonAction, 'invalid entry: %s', entry); + return this.createButtonActionDom_( + /** @type {!goog.tweak.ButtonAction} */ (entry), label); +}; + + + +/** + * Entries used to represent the collapsible namespace links. These entries are + * never registered with the TweakRegistry, but are contained within the + * collection of entries within TweakPanels. + * @param {string} namespace The namespace for the entry. + * @param {!Array} entries Entries within the namespace. + * @constructor + * @extends {goog.tweak.BaseEntry} + * @private + */ +goog.tweak.NamespaceEntry_ = function(namespace, entries) { + goog.tweak.BaseEntry.call( + this, goog.tweak.NamespaceEntry_.ID_PREFIX + namespace, + 'Tweaks within the ' + namespace + ' namespace.'); + + /** + * Entries within this namespace. + * @type {!Array} + */ + this.entries = entries; + + this.label = namespace; +}; +goog.inherits(goog.tweak.NamespaceEntry_, goog.tweak.BaseEntry); + + +/** + * Prefix for the IDs of namespace entries used to ensure that they do not + * conflict with regular entries. + * @type {string} + */ +goog.tweak.NamespaceEntry_.ID_PREFIX = '!'; diff --git a/closure-library/closure/goog/ui/abstractspellchecker.js b/closure-library/closure/goog/ui/abstractspellchecker.js new file mode 100644 index 0000000000..c50e06802c --- /dev/null +++ b/closure-library/closure/goog/ui/abstractspellchecker.js @@ -0,0 +1,1224 @@ +// Copyright 2007 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 Abstract base class for spell checker implementations. + * + * The spell checker supports two modes - synchronous and asynchronous. + * + * In synchronous mode subclass calls processText_ which processes all the text + * given to it before it returns. If the text string is very long, it could + * cause warnings from the browser that considers the script to be + * busy-looping. + * + * Asynchronous mode allows breaking processing large text segments without + * encountering stop script warnings by rescheduling remaining parts of the + * text processing to another stack. + * + * In asynchronous mode abstract spell checker keeps track of a number of text + * chunks that have been processed after the very beginning, and returns every + * so often so that the calling function could reschedule its execution on a + * different stack (for example by calling setInterval(0)). + * + * @author eae@google.com (Emil A Eklund) + */ + +goog.provide('goog.ui.AbstractSpellChecker'); +goog.provide('goog.ui.AbstractSpellChecker.AsyncResult'); + +goog.require('goog.a11y.aria'); +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.InputType'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.classlist'); +goog.require('goog.dom.selection'); +goog.require('goog.events'); +goog.require('goog.events.Event'); +goog.require('goog.events.EventType'); +goog.require('goog.math.Coordinate'); +goog.require('goog.spell.SpellCheck'); +goog.require('goog.structs.Set'); +goog.require('goog.style'); +goog.require('goog.ui.Component'); +goog.require('goog.ui.MenuItem'); +goog.require('goog.ui.MenuSeparator'); +goog.require('goog.ui.PopupMenu'); + + + +/** + * Abstract base class for spell checker editor implementations. Provides basic + * functionality such as word lookup and caching. + * + * @param {goog.spell.SpellCheck} spellCheck Instance of the SpellCheck + * support object to use. A single instance can be shared by multiple editor + * components. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper. + * @constructor + * @extends {goog.ui.Component} + */ +goog.ui.AbstractSpellChecker = function(spellCheck, opt_domHelper) { + goog.ui.Component.call(this, opt_domHelper); + + /** + * Handler to use for caching and lookups. + * @type {goog.spell.SpellCheck} + * @protected + */ + this.spellCheck = spellCheck; + + /** + * Word to element references. Used by replace/ignore. + * @type {Object} + * @private + */ + this.wordElements_ = {}; + + /** + * List of all 'edit word' input elements. + * @type {Array} + * @private + */ + this.inputElements_ = []; + + /** + * Global regular expression for splitting a string into individual words and + * blocks of separators. Matches zero or one word followed by zero or more + * separators. + * @type {RegExp} + * @private + */ + this.splitRegex_ = new RegExp( + '([^' + goog.spell.SpellCheck.WORD_BOUNDARY_CHARS + ']*)' + + '([' + goog.spell.SpellCheck.WORD_BOUNDARY_CHARS + ']*)', + 'g'); + + goog.events.listen( + this.spellCheck, goog.spell.SpellCheck.EventType.WORD_CHANGED, + this.onWordChanged_, false, this); +}; +goog.inherits(goog.ui.AbstractSpellChecker, goog.ui.Component); +goog.tagUnsealableClass(goog.ui.AbstractSpellChecker); + + +/** + * The prefix to mark keys with. + * @type {string} + * @private + */ +goog.ui.AbstractSpellChecker.KEY_PREFIX_ = ':'; + + +/** + * The attribute name for original element contents (to offer subsequent + * correction menu). + * @type {string} + * @private + */ +goog.ui.AbstractSpellChecker.ORIGINAL_ = 'g-spell-original'; + + +/** + * Suggestions menu. + * + * @type {goog.ui.PopupMenu|undefined} + * @private + */ +goog.ui.AbstractSpellChecker.prototype.menu_; + + +/** + * Separator between suggestions and ignore in suggestions menu. + * + * @type {goog.ui.MenuSeparator|undefined} + * @private + */ +goog.ui.AbstractSpellChecker.prototype.menuSeparator_; + + +/** + * Menu item for ignore option. + * + * @type {goog.ui.MenuItem|undefined} + * @private + */ +goog.ui.AbstractSpellChecker.prototype.menuIgnore_; + + +/** + * Menu item for edit word option. + * + * @type {goog.ui.MenuItem|undefined} + * @private + */ +goog.ui.AbstractSpellChecker.prototype.menuEdit_; + + +/** + * Whether the correction UI is visible. + * + * @type {boolean} + * @private + */ +goog.ui.AbstractSpellChecker.prototype.isVisible_ = false; + + +/** + * Cache for corrected words. All corrected words are reverted to their original + * status on resume. Therefore that status is never written to the cache and is + * instead indicated by this set. + * + * @type {goog.structs.Set|undefined} + * @private + */ +goog.ui.AbstractSpellChecker.prototype.correctedWords_; + + +/** + * Class name for suggestions menu. + * + * @type {string} + */ +goog.ui.AbstractSpellChecker.prototype.suggestionsMenuClassName = + goog.getCssName('goog-menu'); + + +/** + * Whether corrected words should be highlighted. + * + * @type {boolean} + */ +goog.ui.AbstractSpellChecker.prototype.markCorrected = false; + + +/** + * Word the correction menu is displayed for. + * + * @type {string|undefined} + * @private + */ +goog.ui.AbstractSpellChecker.prototype.activeWord_; + + +/** + * Element the correction menu is displayed for. + * + * @type {Element|undefined} + * @private + */ +goog.ui.AbstractSpellChecker.prototype.activeElement_; + + +/** + * Indicator that the spell checker is running in the asynchronous mode. + * + * @type {boolean} + * @private + */ +goog.ui.AbstractSpellChecker.prototype.asyncMode_ = false; + + +/** + * Maximum number of words to process on a single stack in asynchronous mode. + * + * @type {number} + * @private + */ +goog.ui.AbstractSpellChecker.prototype.asyncWordsPerBatch_ = 1000; + + +/** + * Current text to process when running in the asynchronous mode. + * + * @type {string|undefined} + * @private + */ +goog.ui.AbstractSpellChecker.prototype.asyncText_; + + +/** + * Current start index of the range that spell-checked correctly. + * + * @type {number|undefined} + * @private + */ +goog.ui.AbstractSpellChecker.prototype.asyncRangeStart_; + + +/** + * Current node with which the asynchronous text is associated. + * + * @type {Node|undefined} + * @private + */ +goog.ui.AbstractSpellChecker.prototype.asyncNode_; + + +/** + * Number of elements processed in the asyncronous mode since last yield. + * + * @type {number} + * @private + */ +goog.ui.AbstractSpellChecker.prototype.processedElementsCount_ = 0; + + +/** + * Markers for the text that does not need to be included in the processing. + * + * For rich text editor this is a list of strings formatted as + * tagName.className or className. If both are specified, the element will be + * excluded if BOTH are matched. If only a className is specified, then we will + * exclude regions with the className. If only one marker is needed, it may be + * passed as a string. + * For plain text editor this is a RegExp that matches the excluded text. + * + * Used exclusively by the derived classes + * + * @type {Array|string|RegExp|undefined} + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.excludeMarker; + + +/** + * Numeric Id of the element that has focus. 0 when not set. + * + * @private {number} + */ +goog.ui.AbstractSpellChecker.prototype.focusedElementIndex_ = 0; + + +/** + * Index for the most recently added misspelled word. + * + * @private {number} + */ +goog.ui.AbstractSpellChecker.prototype.lastIndex_ = 0; + + +/** + * @return {goog.spell.SpellCheck} The handler used for caching and lookups. + */ +goog.ui.AbstractSpellChecker.prototype.getSpellCheck = function() { + return this.spellCheck; +}; + +/** + * Sets the spell checker used for caching and lookups. + * @param {goog.spell.SpellCheck} spellCheck The handler used for caching and + * lookups. + */ +goog.ui.AbstractSpellChecker.prototype.setSpellCheck = function(spellCheck) { + this.spellCheck = spellCheck; +}; + + +/** + * Sets the handler used for caching and lookups. + * @param {goog.spell.SpellCheck} handler The handler used for caching and + * lookups. + * @deprecated Use #setSpellCheck instead. + */ +goog.ui.AbstractSpellChecker.prototype.setHandler = function(handler) { + this.setSpellCheck(handler); +}; + + +/** + * @return {goog.ui.PopupMenu|undefined} The suggestions menu. + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.getMenu = function() { + return this.menu_; +}; + + +/** + * @return {goog.ui.MenuItem|undefined} The menu item for edit word option. + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.getMenuEdit = function() { + return this.menuEdit_; +}; + + +/** + * @return {number} The index of the latest misspelled word to be added. + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.getLastIndex = function() { + return this.lastIndex_; +}; + + +/** + * @return {number} Increments and returns the index for the next misspelled + * word to be added. + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.getNextIndex = function() { + return ++this.lastIndex_; +}; + + +/** + * Sets the marker for the excluded text. + * + * {@see goog.ui.AbstractSpellChecker.prototype.excludeMarker} + * + * @param {Array|string|RegExp|null} marker A RegExp for plain text + * or class names for the rich text spell checker for the elements to + * exclude from checking. + */ +goog.ui.AbstractSpellChecker.prototype.setExcludeMarker = function(marker) { + this.excludeMarker = marker || undefined; +}; + + +/** + * Checks spelling for all text. + * Should be overridden by implementation. + */ +goog.ui.AbstractSpellChecker.prototype.check = function() { + this.isVisible_ = true; + if (this.markCorrected) { + this.correctedWords_ = new goog.structs.Set(); + } +}; + + +/** + * Hides correction UI. + * Should be overridden by implementation. + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.AbstractSpellChecker.prototype.resume = function() { + this.isVisible_ = false; + this.clearWordElements(); + this.lastIndex_ = 0; + this.setFocusedElementIndex(0); + + var input; + while (input = this.inputElements_.pop()) { + input.parentNode.replaceChild( + this.getDomHelper().createTextNode(input.value), input); + } + + if (this.correctedWords_) { + this.correctedWords_.clear(); + } +}; + + +/** + * @return {boolean} Whether the correction ui is visible. + */ +goog.ui.AbstractSpellChecker.prototype.isVisible = function() { + return this.isVisible_; +}; + + +/** + * Clears the word to element references map used by replace/ignore. + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.clearWordElements = function() { + this.wordElements_ = {}; +}; + + +/** + * Ignores spelling of word. + * + * @param {string} word Word to add. + */ +goog.ui.AbstractSpellChecker.prototype.ignoreWord = function(word) { + this.spellCheck.setWordStatus(word, goog.spell.SpellCheck.WordStatus.IGNORED); +}; + + +/** + * Edits a word. + * + * @param {Element} el An element wrapping the word that should be edited. + * @param {string} old Word to edit. + * @private + */ +goog.ui.AbstractSpellChecker.prototype.editWord_ = function(el, old) { + var input = this.getDomHelper().createDom( + goog.dom.TagName.INPUT, {'type': goog.dom.InputType.TEXT, 'value': old}); + var w = goog.style.getSize(el).width; + + // Minimum width to ensure there's always enough room to type. + if (w < 50) { + w = 50; + } + input.style.width = w + 'px'; + el.parentNode.replaceChild(input, el); + try { + input.focus(); + goog.dom.selection.setCursorPosition(input, old.length); + } catch (o) { + } + + this.inputElements_.push(input); +}; + + +/** + * Replaces word. + * + * @param {Element} el An element wrapping the word that should be replaced. + * @param {string} old Word that was replaced. + * @param {string} word Word to replace with. + */ +goog.ui.AbstractSpellChecker.prototype.replaceWord = function(el, old, word) { + if (old != word) { + if (!el.getAttribute(goog.ui.AbstractSpellChecker.ORIGINAL_)) { + el.setAttribute(goog.ui.AbstractSpellChecker.ORIGINAL_, old); + } + goog.dom.setTextContent(el, word); + + var status = this.spellCheck.checkWord(word); + + // Indicate that the word is corrected unless the status is 'INVALID'. + // (if markCorrected is enabled). + if (this.markCorrected && this.correctedWords_ && + status != goog.spell.SpellCheck.WordStatus.INVALID) { + this.correctedWords_.add(word); + status = goog.spell.SpellCheck.WordStatus.CORRECTED; + } + + // Avoid potential collision with the built-in object namespace. For + // example, 'watch' is a reserved name in FireFox. + var oldIndex = goog.ui.AbstractSpellChecker.toInternalKey_(old); + var newIndex = goog.ui.AbstractSpellChecker.toInternalKey_(word); + + // Remove reference between old word and element + var elements = this.wordElements_[oldIndex]; + goog.array.remove(elements, el); + + if (status != goog.spell.SpellCheck.WordStatus.VALID) { + // Create reference between new word and element + if (this.wordElements_[newIndex]) { + this.wordElements_[newIndex].push(el); + } else { + this.wordElements_[newIndex] = [el]; + } + } + + // Update element based on status. + this.updateElement(el, word, status); + + this.dispatchEvent(goog.events.EventType.CHANGE); + } +}; + + +/** + * Retrieves the array of suggested spelling choices. + * + * @return {Array} Suggested spelling choices. + * @private + */ +goog.ui.AbstractSpellChecker.prototype.getSuggestions_ = function() { + // Add new suggestion entries. + var suggestions = this.spellCheck.getSuggestions( + /** @type {string} */ (this.activeWord_)); + if (!suggestions[0]) { + var originalWord = this.activeElement_.getAttribute( + goog.ui.AbstractSpellChecker.ORIGINAL_); + if (originalWord && originalWord != this.activeWord_) { + suggestions = this.spellCheck.getSuggestions(originalWord); + } + } + return suggestions; +}; + + +/** + * Displays suggestions menu. + * @param {Element} el Element to display menu for. + * @param {goog.events.BrowserEvent|goog.math.Coordinate=} opt_pos Position to + * display menu at relative to the viewport (in client coordinates), or a + * mouse event. + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.AbstractSpellChecker.prototype.showSuggestionsMenu = function( + el, opt_pos) { + this.activeWord_ = goog.dom.getTextContent(el); + this.activeElement_ = el; + + // Remove suggestion entries from menu, if any. + while (this.menu_.getChildAt(0) != this.menuSeparator_) { + this.menu_.removeChildAt(0, true).dispose(); + } + + // Add new suggestion entries. + var suggestions = this.getSuggestions_(); + for (var suggestion, i = 0; suggestion = suggestions[i]; i++) { + this.menu_.addChildAt( + new goog.ui.MenuItem(suggestion, suggestion, this.getDomHelper()), i, + true); + } + + if (!suggestions[0]) { + /** @desc Item shown in menu when no suggestions are available. */ + var MSG_SPELL_NO_SUGGESTIONS = goog.getMsg('No Suggestions'); + var item = + new goog.ui.MenuItem(MSG_SPELL_NO_SUGGESTIONS, '', this.getDomHelper()); + item.setEnabled(false); + this.menu_.addChildAt(item, 0, true); + } + + // Show 'Edit word' option if {@link markCorrected} is enabled and don't show + // 'Ignore' option for corrected words. + if (this.markCorrected) { + var corrected = + this.correctedWords_ && this.correctedWords_.contains(this.activeWord_); + this.menuIgnore_.setVisible(!corrected); + this.menuEdit_.setVisible(true); + } else { + this.menuIgnore_.setVisible(true); + this.menuEdit_.setVisible(false); + } + + if (opt_pos) { + if (!(opt_pos instanceof goog.math.Coordinate)) { // it's an event + var posX = opt_pos.clientX; + var posY = opt_pos.clientY; + // Certain implementations which derive from AbstractSpellChecker + // use an iframe in which case the coordinates are relative to + // that iframe's view port. + if (this.getElement().contentDocument || + this.getElement().contentWindow) { + var offset = goog.style.getClientPosition(this.getElement()); + posX += offset.x; + posY += offset.y; + } + opt_pos = new goog.math.Coordinate(posX, posY); + } + this.menu_.showAt(opt_pos.x, opt_pos.y); + } else { + this.menu_.setVisible(true); + } +}; + + +/** + * Initializes suggestions menu. Populates menu with separator and ignore option + * that are always valid. Suggestions are later added above the separator. + * + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.initSuggestionsMenu = function() { + this.menu_ = new goog.ui.PopupMenu(this.getDomHelper()); + this.menuSeparator_ = new goog.ui.MenuSeparator(this.getDomHelper()); + + // Leave alone setAllowAutoFocus at default (true). This allows menu to get + // keyboard focus and thus allowing non-mouse users to get to the menu. + + /** @desc Ignore entry in suggestions menu. */ + var MSG_SPELL_IGNORE = goog.getMsg('Ignore'); + + /** @desc Edit word entry in suggestions menu. */ + var MSG_SPELL_EDIT_WORD = goog.getMsg('Edit Word'); + + this.menu_.addChild(this.menuSeparator_, true); + this.menuIgnore_ = + new goog.ui.MenuItem(MSG_SPELL_IGNORE, '', this.getDomHelper()); + this.menu_.addChild(this.menuIgnore_, true); + this.menuEdit_ = + new goog.ui.MenuItem(MSG_SPELL_EDIT_WORD, '', this.getDomHelper()); + this.menuEdit_.setVisible(false); + this.menu_.addChild(this.menuEdit_, true); + this.menu_.setParent(this); + this.menu_.render(); + + var menuElement = this.menu_.getElement(); + goog.asserts.assert(menuElement); + goog.dom.classlist.add(menuElement, this.suggestionsMenuClassName); + + goog.events.listen( + this.menu_, goog.ui.Component.EventType.ACTION, this.onCorrectionAction, + false, this); +}; + + +/** + * Handles correction menu actions. + * @param {goog.events.Event} event Action event. + * @protected + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.AbstractSpellChecker.prototype.onCorrectionAction = function(event) { + var word = /** @type {string} */ (this.activeWord_); + var el = /** @type {Element} */ (this.activeElement_); + if (event.target == this.menuIgnore_) { + this.ignoreWord(word); + } else if (event.target == this.menuEdit_) { + this.editWord_(el, word); + } else { + this.replaceWord(el, word, event.target.getModel()); + this.dispatchEvent(goog.ui.Component.EventType.CHANGE); + } + + delete this.activeWord_; + delete this.activeElement_; +}; + + +/** + * Removes spell-checker markup and restore the node to text. + * + * @param {Element} el Word element. MUST have a text node child. + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.removeMarkup = function(el) { + var firstChild = el.firstChild; + var text = firstChild.nodeValue; + + if (el.nextSibling && el.nextSibling.nodeType == goog.dom.NodeType.TEXT) { + if (el.previousSibling && + el.previousSibling.nodeType == goog.dom.NodeType.TEXT) { + el.previousSibling.nodeValue = + el.previousSibling.nodeValue + text + el.nextSibling.nodeValue; + this.getDomHelper().removeNode(el.nextSibling); + } else { + el.nextSibling.nodeValue = text + el.nextSibling.nodeValue; + } + } else if ( + el.previousSibling && + el.previousSibling.nodeType == goog.dom.NodeType.TEXT) { + el.previousSibling.nodeValue += text; + } else { + el.parentNode.insertBefore(firstChild, el); + } + + this.getDomHelper().removeNode(el); +}; + + +/** + * Updates element based on word status. Either converts it to a text node, or + * merges it with the previous or next text node if the status of the world is + * VALID, in which case the element itself is eliminated. + * + * @param {Element} el Word element. + * @param {string} word Word to update status for. + * @param {goog.spell.SpellCheck.WordStatus} status Status of word. + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.updateElement = function( + el, word, status) { + if (this.markCorrected && this.correctedWords_ && + this.correctedWords_.contains(word)) { + status = goog.spell.SpellCheck.WordStatus.CORRECTED; + } + if (status == goog.spell.SpellCheck.WordStatus.VALID) { + this.removeMarkup(el); + } else { + goog.dom.setProperties(el, this.getElementProperties(status)); + } +}; + + +/** + * Generates unique Ids for spell checker elements. + * @param {number=} opt_id Id to suffix with. + * @return {string} Unique element id. + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.makeElementId = function(opt_id) { + return this.getId() + '.' + (opt_id ? opt_id : this.getNextIndex()); +}; + + +/** + * Returns the span element that matches the given number index. + * @param {number} index Number index that is used in the element id. + * @return {Element} The matching span element or null if no span matches. + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.getElementByIndex = function(index) { + return this.getDomHelper().getElement(this.makeElementId(index)); +}; + + +/** + * Creates an element for a specified word and stores a reference to it. + * + * @param {string} word Word to create element for. + * @param {goog.spell.SpellCheck.WordStatus} status Status of word. + * @return {!HTMLSpanElement} The created element. + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.createWordElement = function( + word, status) { + var parameters = this.getElementProperties(status); + + // Add id & tabindex as necessary. + if (!parameters['id']) { + parameters['id'] = this.makeElementId(); + } + if (!parameters['tabIndex']) { + parameters['tabIndex'] = -1; + } + + var el = + this.getDomHelper().createDom(goog.dom.TagName.SPAN, parameters, word); + goog.a11y.aria.setRole(el, 'menuitem'); + goog.a11y.aria.setState(el, 'haspopup', true); + this.registerWordElement(word, el); + + return el; +}; + + +/** + * Stores a reference to word element. + * + * @param {string} word The word to store. + * @param {HTMLSpanElement} el The element associated with it. + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.registerWordElement = function( + word, el) { + // Avoid potential collision with the built-in object namespace. For + // example, 'watch' is a reserved name in FireFox. + var index = goog.ui.AbstractSpellChecker.toInternalKey_(word); + if (this.wordElements_[index]) { + this.wordElements_[index].push(el); + } else { + this.wordElements_[index] = [el]; + } +}; + + +/** + * Returns desired element properties for the specified status. + * Should be overridden by implementation. + * + * @param {goog.spell.SpellCheck.WordStatus} status Status of word. + * @return {Object} Properties to apply to the element. + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.getElementProperties = + goog.abstractMethod; + + +/** + * Handles word change events and updates the word elements accordingly. + * + * @param {goog.spell.SpellCheck.WordChangedEvent} event The event object. + * @private + */ +goog.ui.AbstractSpellChecker.prototype.onWordChanged_ = function(event) { + // Avoid potential collision with the built-in object namespace. For + // example, 'watch' is a reserved name in FireFox. + var index = goog.ui.AbstractSpellChecker.toInternalKey_(event.word); + var elements = this.wordElements_[index]; + if (elements) { + for (var el, i = 0; el = elements[i]; i++) { + this.updateElement(el, event.word, event.status); + } + } +}; + + +/** @override */ +goog.ui.AbstractSpellChecker.prototype.disposeInternal = function() { + if (this.isVisible_) { + // Clears wordElements_ + this.resume(); + } + + goog.events.unlisten( + this.spellCheck, goog.spell.SpellCheck.EventType.WORD_CHANGED, + this.onWordChanged_, false, this); + + if (this.menu_) { + this.menu_.dispose(); + delete this.menu_; + delete this.menuIgnore_; + delete this.menuSeparator_; + } + delete this.spellCheck; + delete this.wordElements_; + + goog.ui.AbstractSpellChecker.superClass_.disposeInternal.call(this); +}; + + +/** + * Precharges local dictionary cache. This is optional, but greatly reduces + * amount of subsequent churn in the DOM tree because most of the words become + * known from the very beginning. + * + * @param {string} text Text to process. + * @param {number} words Max number of words to scan. + * @return {number} number of words actually scanned. + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.populateDictionary = function( + text, words) { + this.splitRegex_.lastIndex = 0; + var result; + var numScanned = 0; + while (result = this.splitRegex_.exec(text)) { + if (result[0].length == 0) { + break; + } + var word = result[1]; + if (word) { + this.spellCheck.checkWord(word); + ++numScanned; + if (numScanned >= words) { + break; + } + } + } + this.spellCheck.processPending(); + return numScanned; +}; + + +/** + * Processes word. + * Should be overridden by implementation. + * + * @param {Node} node Node containing word. + * @param {string} text Word to process. + * @param {goog.spell.SpellCheck.WordStatus} status Status of the word. + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.processWord = function( + node, text, status) { + throw new Error('Need to override processWord_ in derivative class'); +}; + + +/** + * Processes range of text that checks out (contains no unrecognized words). + * Should be overridden by implementation. May contain words and separators. + * + * @param {Node} node Node containing text range. + * @param {string} text text to process. + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.processRange = function(node, text) { + throw new Error('Need to override processRange_ in derivative class'); +}; + + +/** + * Starts asynchronous processing mode. + * + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.initializeAsyncMode = function() { + if (this.asyncMode_ || this.processedElementsCount_ || + this.asyncText_ != null || this.asyncNode_) { + throw new Error('Async mode already in progress.'); + } + this.asyncMode_ = true; + this.processedElementsCount_ = 0; + delete this.asyncText_; + this.asyncRangeStart_ = 0; + delete this.asyncNode_; + + this.blockReadyEvents(); +}; + + +/** + * Finalizes asynchronous processing mode. Should be called after there is no + * more text to process and processTextAsync and/or continueAsyncProcessing + * returned FINISHED. + * + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.finishAsyncProcessing = function() { + if (!this.asyncMode_ || this.asyncText_ != null || this.asyncNode_) { + throw new Error( + 'Async mode not started or there is still text to process.'); + } + this.asyncMode_ = false; + this.processedElementsCount_ = 0; + + this.unblockReadyEvents(); + this.spellCheck.processPending(); +}; + + +/** + * Blocks processing of spell checker READY events. This is used in dictionary + * recharge and async mode so that completion is not signaled prematurely. + * + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.blockReadyEvents = function() { + goog.events.listen( + this.spellCheck, goog.spell.SpellCheck.EventType.READY, + goog.events.Event.stopPropagation, true); +}; + + +/** + * Unblocks processing of spell checker READY events. This is used in + * dictionary recharge and async mode so that completion is not signaled + * prematurely. + * + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.unblockReadyEvents = function() { + goog.events.unlisten( + this.spellCheck, goog.spell.SpellCheck.EventType.READY, + goog.events.Event.stopPropagation, true); +}; + + +/** + * Splits text into individual words and blocks of separators. Calls virtual + * processWord_ and processRange_ methods. + * + * @param {Node} node Node containing text. + * @param {string} text Text to process. + * @return {goog.ui.AbstractSpellChecker.AsyncResult} operation result. + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.processTextAsync = function(node, text) { + if (!this.asyncMode_ || this.asyncText_ != null || this.asyncNode_) { + throw new Error( + 'Not in async mode or previous text has not been processed.'); + } + + this.splitRegex_.lastIndex = 0; + var stringSegmentStart = 0; + + var result; + while (result = this.splitRegex_.exec(text)) { + if (result[0].length == 0) { + break; + } + var word = result[1]; + if (word) { + var status = this.spellCheck.checkWord(word); + if (status != goog.spell.SpellCheck.WordStatus.VALID) { + var precedingText = + text.substr(stringSegmentStart, result.index - stringSegmentStart); + if (precedingText) { + this.processRange(node, precedingText); + } + stringSegmentStart = result.index + word.length; + this.processWord(node, word, status); + } + } + this.processedElementsCount_++; + if (this.processedElementsCount_ > this.asyncWordsPerBatch_) { + this.asyncText_ = text; + this.asyncRangeStart_ = stringSegmentStart; + this.asyncNode_ = node; + this.processedElementsCount_ = 0; + return goog.ui.AbstractSpellChecker.AsyncResult.PENDING; + } + } + + var leftoverText = text.substr(stringSegmentStart); + if (leftoverText) { + this.processRange(node, leftoverText); + } + + return goog.ui.AbstractSpellChecker.AsyncResult.DONE; +}; + + +/** + * Continues processing started by processTextAsync. Calls virtual + * processWord_ and processRange_ methods. + * + * @return {goog.ui.AbstractSpellChecker.AsyncResult} operation result. + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.continueAsyncProcessing = function() { + if (!this.asyncMode_ || this.asyncText_ == null || !this.asyncNode_) { + throw new Error('Not in async mode or processing not started.'); + } + var node = /** @type {Node} */ (this.asyncNode_); + var stringSegmentStart = this.asyncRangeStart_; + goog.asserts.assertNumber(stringSegmentStart); + var text = this.asyncText_; + + var result; + while (result = this.splitRegex_.exec(text)) { + if (result[0].length == 0) { + break; + } + var word = result[1]; + if (word) { + var status = this.spellCheck.checkWord(word); + if (status != goog.spell.SpellCheck.WordStatus.VALID) { + var precedingText = + text.substr(stringSegmentStart, result.index - stringSegmentStart); + if (precedingText) { + this.processRange(node, precedingText); + } + stringSegmentStart = result.index + word.length; + this.processWord(node, word, status); + } + } + this.processedElementsCount_++; + if (this.processedElementsCount_ > this.asyncWordsPerBatch_) { + this.processedElementsCount_ = 0; + this.asyncRangeStart_ = stringSegmentStart; + return goog.ui.AbstractSpellChecker.AsyncResult.PENDING; + } + } + delete this.asyncText_; + this.asyncRangeStart_ = 0; + delete this.asyncNode_; + + var leftoverText = text.substr(stringSegmentStart); + if (leftoverText) { + this.processRange(node, leftoverText); + } + + return goog.ui.AbstractSpellChecker.AsyncResult.DONE; +}; + + +/** + * Converts a word to an internal key representation. This is necessary to + * avoid collisions with object's internal namespace. Only words that are + * reserved need to be escaped. + * + * @param {string} word The word to map. + * @return {string} The index. + * @private + */ +goog.ui.AbstractSpellChecker.toInternalKey_ = function(word) { + if (word in Object.prototype) { + return goog.ui.AbstractSpellChecker.KEY_PREFIX_ + word; + } + return word; +}; + + +/** + * Navigate keyboard focus in the given direction. + * + * @param {goog.ui.AbstractSpellChecker.Direction} direction The direction to + * navigate in. + * @return {boolean} Whether the action is handled here. If not handled + * here, the initiating event may be propagated. + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.navigate = function(direction) { + var handled = false; + var isMovingToNextWord = + direction == goog.ui.AbstractSpellChecker.Direction.NEXT; + var focusedIndex = this.getFocusedElementIndex(); + + var el; + do { + // Determine new index based on given direction. + focusedIndex += isMovingToNextWord ? 1 : -1; + + if (focusedIndex < 1 || focusedIndex > this.getLastIndex()) { + // Exit the loop, because this focusedIndex cannot have an element. + handled = true; + break; + } + + // Word elements are removed during the correction action. If no element is + // found for the new focusedIndex, then try again with the next value. + } while (!(el = this.getElementByIndex(focusedIndex))); + + if (el) { + this.setFocusedElementIndex(focusedIndex); + this.focusOnElement(el); + handled = true; + } + + return handled; +}; + + +/** + * Returns the index of the currently focussed invalid word element. This index + * starts at one instead of zero. + * + * @return {number} the index of the currently focussed element + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.getFocusedElementIndex = function() { + return this.focusedElementIndex_; +}; + + +/** + * Sets the index of the currently focussed invalid word element. This index + * should start at one instead of zero. + * + * @param {number} focusElementIndex the index of the currently focussed element + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.setFocusedElementIndex = function( + focusElementIndex) { + this.focusedElementIndex_ = focusElementIndex; +}; + + +/** + * Sets the focus on the provided word element. + * + * @param {Element} element The word element that should receive focus. + * @protected + */ +goog.ui.AbstractSpellChecker.prototype.focusOnElement = function(element) { + element.focus(); +}; + + +/** + * Constants for representing the direction while navigating. + * + * @enum {number} + */ +goog.ui.AbstractSpellChecker.Direction = { + PREVIOUS: 0, + NEXT: 1 +}; + + +/** + * Constants for the result of asynchronous processing. + * @enum {number} + */ +goog.ui.AbstractSpellChecker.AsyncResult = { + /** + * Caller must reschedule operation and call continueAsyncProcessing on the + * new stack frame. + */ + PENDING: 1, + /** + * Current element has been fully processed. Caller can call + * processTextAsync or finishAsyncProcessing. + */ + DONE: 2 +}; diff --git a/closure-library/closure/goog/ui/ac/ac.js b/closure-library/closure/goog/ui/ac/ac.js new file mode 100644 index 0000000000..41206bbc8d --- /dev/null +++ b/closure-library/closure/goog/ui/ac/ac.js @@ -0,0 +1,51 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utility methods supporting the autocomplete package. + * + * @author adamwos@google.com (Adam Wos) + * @see ../../demos/autocomplete-basic.html + */ + +goog.provide('goog.ui.ac'); + +goog.require('goog.ui.ac.ArrayMatcher'); +goog.require('goog.ui.ac.AutoComplete'); +goog.require('goog.ui.ac.InputHandler'); +goog.require('goog.ui.ac.Renderer'); + + +/** + * Factory function for building a basic autocomplete widget that autocompletes + * an inputbox or text area from a data array. + * @param {Array} data Data array. + * @param {Element} input Input element or text area. + * @param {boolean=} opt_multi Whether to allow multiple entries separated with + * semi-colons or commas. + * @param {boolean=} opt_useSimilar use similar matches. e.g. "gost" => "ghost". + * @return {!goog.ui.ac.AutoComplete} A new autocomplete object. + */ +goog.ui.ac.createSimpleAutoComplete = function( + data, input, opt_multi, opt_useSimilar) { + var matcher = new goog.ui.ac.ArrayMatcher(data, !opt_useSimilar); + var renderer = new goog.ui.ac.Renderer(); + var inputHandler = new goog.ui.ac.InputHandler(null, null, !!opt_multi); + + var autoComplete = + new goog.ui.ac.AutoComplete(matcher, renderer, inputHandler); + inputHandler.attachAutoComplete(autoComplete); + inputHandler.attachInputs(input); + return autoComplete; +}; diff --git a/closure-library/closure/goog/ui/ac/arraymatcher.js b/closure-library/closure/goog/ui/ac/arraymatcher.js new file mode 100644 index 0000000000..21db2deecd --- /dev/null +++ b/closure-library/closure/goog/ui/ac/arraymatcher.js @@ -0,0 +1,212 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Basic class for matching words in an array. + * + */ + + +goog.provide('goog.ui.ac.ArrayMatcher'); + +goog.require('goog.string'); + + + +/** + * Basic class for matching words in an array + * @constructor + * @param {Array} rows Dictionary of items to match. Can be objects if they + * have a toString method that returns the value to match against. + * @param {boolean=} opt_noSimilar if true, do not do similarity matches for the + * input token against the dictionary. + */ +goog.ui.ac.ArrayMatcher = function(rows, opt_noSimilar) { + this.rows_ = rows || []; + this.useSimilar_ = !opt_noSimilar; +}; + + +/** + * Replaces the rows that this object searches over. + * @param {Array} rows Dictionary of items to match. + */ +goog.ui.ac.ArrayMatcher.prototype.setRows = function(rows) { + this.rows_ = rows || []; +}; + + +/** + * Function used to pass matches to the autocomplete + * @param {string} token Token to match. + * @param {number} maxMatches Max number of matches to return. + * @param {Function} matchHandler callback to execute after matching. + * @param {string=} opt_fullString The full string from the input box. + */ +goog.ui.ac.ArrayMatcher.prototype.requestMatchingRows = function( + token, maxMatches, matchHandler, opt_fullString) { + + var matches = this.useSimilar_ ? + goog.ui.ac.ArrayMatcher.getMatchesForRows(token, maxMatches, this.rows_) : + this.getPrefixMatches(token, maxMatches); + + matchHandler(token, matches); +}; + + +/** + * Matches the token against the specified rows, first looking for prefix + * matches and if that fails, then looking for similar matches. + * + * @param {string} token Token to match. + * @param {number} maxMatches Max number of matches to return. + * @param {!Array} rows Rows to search for matches. Can be objects if they + * have a toString method that returns the value to match against. + * @return {!Array} Rows that match. + */ +goog.ui.ac.ArrayMatcher.getMatchesForRows = function(token, maxMatches, rows) { + var matches = + goog.ui.ac.ArrayMatcher.getPrefixMatchesForRows(token, maxMatches, rows); + + if (matches.length == 0) { + matches = goog.ui.ac.ArrayMatcher.getSimilarMatchesForRows( + token, maxMatches, rows); + } + return matches; +}; + + +/** + * Matches the token against the start of words in the row. + * @param {string} token Token to match. + * @param {number} maxMatches Max number of matches to return. + * @return {!Array} Rows that match. + */ +goog.ui.ac.ArrayMatcher.prototype.getPrefixMatches = function( + token, maxMatches) { + return goog.ui.ac.ArrayMatcher.getPrefixMatchesForRows( + token, maxMatches, this.rows_); +}; + + +/** + * Matches the token against the start of words in the row. + * @param {string} token Token to match. + * @param {number} maxMatches Max number of matches to return. + * @param {!Array} rows Rows to search for matches. Can be objects if they + * have + * a toString method that returns the value to match against. + * @return {!Array} Rows that match. + */ +goog.ui.ac.ArrayMatcher.getPrefixMatchesForRows = function( + token, maxMatches, rows) { + var matches = []; + + if (token != '') { + var escapedToken = goog.string.regExpEscape(token); + var matcher = new RegExp('(^|\\W+)' + escapedToken, 'i'); + + for (var i = 0; i < rows.length && matches.length < maxMatches; i++) { + var row = rows[i]; + if (String(row).match(matcher)) { + matches.push(row); + } + } + } + return matches; +}; + + +/** + * Matches the token against similar rows, by calculating "distance" between the + * terms. + * @param {string} token Token to match. + * @param {number} maxMatches Max number of matches to return. + * @return {!Array} The best maxMatches rows. + */ +goog.ui.ac.ArrayMatcher.prototype.getSimilarRows = function(token, maxMatches) { + return goog.ui.ac.ArrayMatcher.getSimilarMatchesForRows( + token, maxMatches, this.rows_); +}; + + +/** + * Matches the token against similar rows, by calculating "distance" between the + * terms. + * @param {string} token Token to match. + * @param {number} maxMatches Max number of matches to return. + * @param {!Array} rows Rows to search for matches. Can be objects + * if they have a toString method that returns the value to + * match against. + * @return {!Array} The best maxMatches rows. + */ +goog.ui.ac.ArrayMatcher.getSimilarMatchesForRows = function( + token, maxMatches, rows) { + var results = []; + + for (var index = 0; index < rows.length; index++) { + var row = rows[index]; + var str = token.toLowerCase(); + var txt = String(row).toLowerCase(); + var score = 0; + + if (txt.indexOf(str) != -1) { + score = parseInt((txt.indexOf(str) / 4).toString(), 10); + + } else { + var arr = str.split(''); + + var lastPos = -1; + var penalty = 10; + + for (var i = 0, c; c = arr[i]; i++) { + var pos = txt.indexOf(c); + + if (pos > lastPos) { + var diff = pos - lastPos - 1; + + if (diff > penalty - 5) { + diff = penalty - 5; + } + + score += diff; + + lastPos = pos; + } else { + score += penalty; + penalty += 5; + } + } + } + + if (score < str.length * 6) { + results.push({str: row, score: score, index: index}); + } + } + + results.sort(function(a, b) { + var diff = a.score - b.score; + if (diff != 0) { + return diff; + } + return a.index - b.index; + }); + + var matches = []; + for (var i = 0; i < maxMatches && i < results.length; i++) { + matches.push(results[i].str); + } + + return matches; +}; diff --git a/closure-library/closure/goog/ui/ac/autocomplete.js b/closure-library/closure/goog/ui/ac/autocomplete.js new file mode 100644 index 0000000000..9252fcdeb4 --- /dev/null +++ b/closure-library/closure/goog/ui/ac/autocomplete.js @@ -0,0 +1,920 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Gmail-like AutoComplete logic. + * + * @see ../../demos/autocomplete-basic.html + */ + +goog.provide('goog.ui.ac.AutoComplete'); +goog.provide('goog.ui.ac.AutoComplete.EventType'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.events'); +goog.require('goog.events.EventTarget'); +goog.require('goog.object'); +goog.require('goog.ui.ac.RenderOptions'); + +goog.forwardDeclare('goog.ui.ac.InputHandler'); + + +/** + * This is the central manager class for an AutoComplete instance. The matcher + * can specify disabled rows that should not be hilited or selected by + * implementing isRowDisabled(row):boolean for each autocomplete + * row. No row will be considered disabled if this method is not implemented. + * + * @param {Object} matcher A data source and row matcher, implements + * requestMatchingRows(token, maxMatches, matchCallback). + * @param {goog.events.EventTarget} renderer An object that implements + * + * isVisible():boolean
      + * renderRows(rows:Array, token:string, target:Element);
      + * hiliteId(row-id:number);
      + * dismiss();
      + * dispose(): + *
      . + * @param {Object} selectionHandler An object that implements + * + * selectRow(row);
      + * update(opt_force); + *
      . + * + * @constructor + * @extends {goog.events.EventTarget} + * @suppress {underscore} + */ +goog.ui.ac.AutoComplete = function(matcher, renderer, selectionHandler) { + goog.events.EventTarget.call(this); + + /** + * A data-source which provides autocomplete suggestions. + * + * TODO(chrishenry): Tighten the type to !goog.ui.ac.AutoComplete.Matcher. + * + * @type {Object} + * @protected + * @suppress {underscore|visibility} + */ + this.matcher_ = matcher; + + /** + * A handler which interacts with the input DOM element (textfield, textarea, + * or richedit). + * + * TODO(chrishenry): Tighten the type to !Object. + * + * @type {Object} + * @protected + * @suppress {underscore|visibility} + */ + this.selectionHandler_ = selectionHandler; + + /** + * A renderer to render/show/highlight/hide the autocomplete menu. + * @type {goog.events.EventTarget} + * @protected + * @suppress {underscore|visibility} + */ + this.renderer_ = renderer; + goog.events.listen( + renderer, + [ + goog.ui.ac.AutoComplete.EventType.HILITE, + goog.ui.ac.AutoComplete.EventType.SELECT, + goog.ui.ac.AutoComplete.EventType.CANCEL_DISMISS, + goog.ui.ac.AutoComplete.EventType.DISMISS + ], + this.handleEvent, false, this); + + /** + * Currently typed token which will be used for completion. + * @type {?string} + * @protected + * @suppress {underscore|visibility} + */ + this.token_ = null; + + /** + * Autocomplete suggestion items. + * @type {Array} + * @protected + * @suppress {underscore|visibility} + */ + this.rows_ = []; + + /** + * Id of the currently highlighted row. + * @type {number} + * @protected + * @suppress {underscore|visibility} + */ + this.hiliteId_ = -1; + + /** + * Id of the first row in autocomplete menu. Note that new ids are assigned + * every time new suggestions are fetched. + * + * TODO(chrishenry): Figure out what subclass does with this value + * and whether we should expose a more proper API. + * + * @type {number} + * @protected + * @suppress {underscore|visibility} + */ + this.firstRowId_ = 0; + + /** + * The target HTML node for displaying. + * @type {?Element} + * @protected + * @suppress {underscore|visibility} + */ + this.target_ = null; + + /** + * The timer id for dismissing autocomplete menu with a delay. + * @type {?number} + * @private + */ + this.dismissTimer_ = null; + + /** + * Mapping from text input element to the anchor element. If the + * mapping does not exist, the input element will act as the anchor + * element. + * @type {Object} + * @private + */ + this.inputToAnchorMap_ = {}; +}; +goog.inherits(goog.ui.ac.AutoComplete, goog.events.EventTarget); + + +/** + * The maximum number of matches that should be returned + * @type {number} + * @private + */ +goog.ui.ac.AutoComplete.prototype.maxMatches_ = 10; + + +/** + * True iff the first row should automatically be highlighted + * @type {boolean} + * @private + */ +goog.ui.ac.AutoComplete.prototype.autoHilite_ = true; + + +/** + * True iff the user can unhilight all rows by pressing the up arrow. + * @type {boolean} + * @private + */ +goog.ui.ac.AutoComplete.prototype.allowFreeSelect_ = false; + + +/** + * True iff item selection should wrap around from last to first. If + * allowFreeSelect_ is on in conjunction, there is a step of free selection + * before wrapping. + * @type {boolean} + * @private + */ +goog.ui.ac.AutoComplete.prototype.wrap_ = false; + + +/** + * Whether completion from suggestion triggers fetching new suggestion. + * @type {boolean} + * @private + */ +goog.ui.ac.AutoComplete.prototype.triggerSuggestionsOnUpdate_ = false; + + +/** + * Events associated with the autocomplete + * @enum {string} + */ +goog.ui.ac.AutoComplete.EventType = { + + /** A row has been highlighted by the renderer */ + ROW_HILITE: 'rowhilite', + + // Note: The events below are used for internal autocomplete events only and + // should not be used in non-autocomplete code. + + /** A row has been mouseovered and should be highlighted by the renderer. */ + HILITE: 'hilite', + + /** A row has been selected by the renderer */ + SELECT: 'select', + + /** A dismiss event has occurred */ + DISMISS: 'dismiss', + + /** Event that cancels a dismiss event */ + CANCEL_DISMISS: 'canceldismiss', + + /** + * Field value was updated. A row field is included and is non-null when a + * row has been selected. The value of the row typically includes fields: + * contactData and formattedValue as well as a toString function (though none + * of these fields are guaranteed to exist). The row field may be used to + * return custom-type row data. + */ + UPDATE: 'update', + + /** + * The list of suggestions has been updated, usually because either the list + * has opened, or because the user has typed another character and the + * suggestions have been updated, or the user has dismissed the autocomplete. + */ + SUGGESTIONS_UPDATE: 'suggestionsupdate' +}; + + +/** + * @typedef {{ + * requestMatchingRows:(!Function|undefined), + * isRowDisabled:(!Function|undefined) + * }} + */ +goog.ui.ac.AutoComplete.Matcher; + + +/** + * @return {!Object} The data source providing the `autocomplete + * suggestions. + */ +goog.ui.ac.AutoComplete.prototype.getMatcher = function() { + return goog.asserts.assert(this.matcher_); +}; + + +/** + * Sets the data source providing the autocomplete suggestions. + * + * See constructor documentation for the interface. + * + * @param {!Object} matcher The matcher. + * @protected + */ +goog.ui.ac.AutoComplete.prototype.setMatcher = function(matcher) { + this.matcher_ = matcher; +}; + + +/** + * @return {!Object} The handler used to interact with the input DOM + * element (textfield, textarea, or richedit), e.g. to update the + * input DOM element with selected value. + * @protected + */ +goog.ui.ac.AutoComplete.prototype.getSelectionHandler = function() { + return goog.asserts.assert(this.selectionHandler_); +}; + + +/** + * @return {goog.events.EventTarget} The renderer that + * renders/shows/highlights/hides the autocomplete menu. + * See constructor documentation for the expected renderer API. + */ +goog.ui.ac.AutoComplete.prototype.getRenderer = function() { + return this.renderer_; +}; + + +/** + * Sets the renderer that renders/shows/highlights/hides the autocomplete + * menu. + * + * See constructor documentation for the expected renderer API. + * + * @param {goog.events.EventTarget} renderer The renderer. + * @protected + */ +goog.ui.ac.AutoComplete.prototype.setRenderer = function(renderer) { + this.renderer_ = renderer; +}; + + +/** + * @return {?string} The currently typed token used for completion. + * @protected + */ +goog.ui.ac.AutoComplete.prototype.getToken = function() { + return this.token_; +}; + + +/** + * Sets the current token (without changing the rendered autocompletion). + * + * NOTE(chrishenry): This method will likely go away when we figure + * out a better API. + * + * @param {?string} token The new token. + * @protected + */ +goog.ui.ac.AutoComplete.prototype.setTokenInternal = function(token) { + this.token_ = token; +}; + + +/** + * @param {number} index The suggestion index, must be within the + * interval [0, this.getSuggestionCount()). + * @return {Object} The currently suggested item at the given index + * (or null if there is none). + */ +goog.ui.ac.AutoComplete.prototype.getSuggestion = function(index) { + return this.rows_[index]; +}; + + +/** + * @return {!Array} The current autocomplete suggestion items. + */ +goog.ui.ac.AutoComplete.prototype.getAllSuggestions = function() { + return goog.asserts.assert(this.rows_); +}; + + +/** + * @return {number} The number of currently suggested items. + */ +goog.ui.ac.AutoComplete.prototype.getSuggestionCount = function() { + return this.rows_.length; +}; + + +/** + * @return {number} The id (not index!) of the currently highlighted row. + */ +goog.ui.ac.AutoComplete.prototype.getHighlightedId = function() { + return this.hiliteId_; +}; + + +/** + * Generic event handler that handles any events this object is listening to. + * @param {goog.events.Event} e Event Object. + */ +goog.ui.ac.AutoComplete.prototype.handleEvent = function(e) { + var matcher = /** @type {?goog.ui.ac.AutoComplete.Matcher} */ (this.matcher_); + + if (e.target == this.renderer_) { + switch (e.type) { + case goog.ui.ac.AutoComplete.EventType.HILITE: + this.hiliteId(/** @type {number} */ (e.row)); + break; + + case goog.ui.ac.AutoComplete.EventType.SELECT: + var rowDisabled = false; + + // e.row can be either a valid row id or empty. + if (goog.isNumber(e.row)) { + var rowId = e.row; + var index = this.getIndexOfId(rowId); + var row = this.rows_[index]; + + // Make sure the row selected is not a disabled row. + rowDisabled = + !!row && matcher.isRowDisabled && matcher.isRowDisabled(row); + if (row && !rowDisabled && this.hiliteId_ != rowId) { + // Event target row not currently highlighted - fix the mismatch. + this.hiliteId(rowId); + } + } + if (!rowDisabled) { + // Note that rowDisabled can be false even if e.row does not + // contain a valid row ID; at least one client depends on us + // proceeding anyway. + this.selectHilited(); + } + break; + + case goog.ui.ac.AutoComplete.EventType.CANCEL_DISMISS: + this.cancelDelayedDismiss(); + break; + + case goog.ui.ac.AutoComplete.EventType.DISMISS: + this.dismissOnDelay(); + break; + } + } +}; + + +/** + * Sets the max number of matches to fetch from the Matcher. + * + * @param {number} max Max number of matches. + */ +goog.ui.ac.AutoComplete.prototype.setMaxMatches = function(max) { + this.maxMatches_ = max; +}; + + +/** + * Sets whether or not the first row should be highlighted by default. + * + * @param {boolean} autoHilite true iff the first row should be + * highlighted by default. + */ +goog.ui.ac.AutoComplete.prototype.setAutoHilite = function(autoHilite) { + this.autoHilite_ = autoHilite; +}; + + +/** + * Sets whether or not the up/down arrow can unhilite all rows. + * + * @param {boolean} allowFreeSelect true iff the up arrow can unhilite all rows. + */ +goog.ui.ac.AutoComplete.prototype.setAllowFreeSelect = function( + allowFreeSelect) { + this.allowFreeSelect_ = allowFreeSelect; +}; + + +/** + * Sets whether or not selections can wrap around the edges. + * + * @param {boolean} wrap true iff sections should wrap around the edges. + */ +goog.ui.ac.AutoComplete.prototype.setWrap = function(wrap) { + this.wrap_ = wrap; +}; + + +/** + * Sets whether or not to request new suggestions immediately after completion + * of a suggestion. + * + * @param {boolean} triggerSuggestionsOnUpdate true iff completion should fetch + * new suggestions. + */ +goog.ui.ac.AutoComplete.prototype.setTriggerSuggestionsOnUpdate = function( + triggerSuggestionsOnUpdate) { + this.triggerSuggestionsOnUpdate_ = triggerSuggestionsOnUpdate; +}; + + +/** + * Sets the token to match against. This triggers calls to the Matcher to + * fetch the matches (up to maxMatches), and then it triggers a call to + * renderer.renderRows(). + * + * @param {string} token The string for which to search in the Matcher. + * @param {string=} opt_fullString Optionally, the full string in the input + * field. + */ +goog.ui.ac.AutoComplete.prototype.setToken = function(token, opt_fullString) { + if (this.token_ == token) { + return; + } + this.token_ = token; + this.matcher_.requestMatchingRows( + this.token_, this.maxMatches_, goog.bind(this.matchListener_, this), + opt_fullString); + this.cancelDelayedDismiss(); +}; + + +/** + * Gets the current target HTML node for displaying autocomplete UI. + * @return {Element} The current target HTML node for displaying autocomplete + * UI. + */ +goog.ui.ac.AutoComplete.prototype.getTarget = function() { + return this.target_; +}; + + +/** + * Sets the current target HTML node for displaying autocomplete UI. + * Can be an implementation specific definition of how to display UI in relation + * to the target node. + * This target will be passed into renderer.renderRows() + * + * @param {Element} target The current target HTML node for displaying + * autocomplete UI. + */ +goog.ui.ac.AutoComplete.prototype.setTarget = function(target) { + this.target_ = target; +}; + + +/** + * @return {boolean} Whether the autocomplete's renderer is open. + */ +goog.ui.ac.AutoComplete.prototype.isOpen = function() { + return this.renderer_.isVisible(); +}; + + +/** + * @return {number} Number of rows in the autocomplete. + * @deprecated Use this.getSuggestionCount(). + */ +goog.ui.ac.AutoComplete.prototype.getRowCount = function() { + return this.getSuggestionCount(); +}; + + +/** + * Moves the hilite to the next non-disabled row. + * Calls renderer.hiliteId() when there's something to do. + * @return {boolean} Returns true on a successful hilite. + */ +goog.ui.ac.AutoComplete.prototype.hiliteNext = function() { + var lastId = this.firstRowId_ + this.rows_.length - 1; + var toHilite = this.hiliteId_; + // Hilite the next row, skipping any disabled rows. + for (var i = 0; i < this.rows_.length; i++) { + // Increment to the next row. + if (toHilite >= this.firstRowId_ && toHilite < lastId) { + toHilite++; + } else if (toHilite == -1) { + toHilite = this.firstRowId_; + } else if (this.allowFreeSelect_ && toHilite == lastId) { + this.hiliteId(-1); + return false; + } else if (this.wrap_ && toHilite == lastId) { + toHilite = this.firstRowId_; + } else { + return false; + } + + if (this.hiliteId(toHilite)) { + return true; + } + } + return false; +}; + + +/** + * Moves the hilite to the previous non-disabled row. Calls + * renderer.hiliteId() when there's something to do. + * @return {boolean} Returns true on a successful hilite. + */ +goog.ui.ac.AutoComplete.prototype.hilitePrev = function() { + var lastId = this.firstRowId_ + this.rows_.length - 1; + var toHilite = this.hiliteId_; + // Hilite the previous row, skipping any disabled rows. + for (var i = 0; i < this.rows_.length; i++) { + // Decrement to the previous row. + if (toHilite > this.firstRowId_) { + toHilite--; + } else if (this.allowFreeSelect_ && toHilite == this.firstRowId_) { + this.hiliteId(-1); + return false; + } else if (this.wrap_ && (toHilite == -1 || toHilite == this.firstRowId_)) { + toHilite = lastId; + } else { + return false; + } + + if (this.hiliteId(toHilite)) { + return true; + } + } + return false; +}; + + +/** + * Hilites the id if it's valid and the row is not disabled, otherwise does + * nothing. + * @param {number} id A row id (not index). + * @return {boolean} Whether the id was hilited. Returns false if the row is + * disabled. + */ +goog.ui.ac.AutoComplete.prototype.hiliteId = function(id) { + var index = this.getIndexOfId(id); + var row = this.rows_[index]; + var rowDisabled = + !!row && this.matcher_.isRowDisabled && this.matcher_.isRowDisabled(row); + if (!rowDisabled) { + this.hiliteId_ = id; + this.renderer_.hiliteId(id); + return index != -1; + } + return false; +}; + + +/** + * Hilites the index, if it's valid and the row is not disabled, otherwise does + * nothing. + * @param {number} index The row's index. + * @return {boolean} Whether the index was hilited. + */ +goog.ui.ac.AutoComplete.prototype.hiliteIndex = function(index) { + return this.hiliteId(this.getIdOfIndex_(index)); +}; + + +/** + * If there are any current matches, this passes the hilited row data to + * selectionHandler.selectRow() + * @return {boolean} Whether there are any current matches. + */ +goog.ui.ac.AutoComplete.prototype.selectHilited = function() { + var index = this.getIndexOfId(this.hiliteId_); + if (index != -1) { + var selectedRow = this.rows_[index]; + var suppressUpdate = this.selectionHandler_.selectRow(selectedRow); + if (this.triggerSuggestionsOnUpdate_) { + this.token_ = null; + this.dismissOnDelay(); + } else { + this.dismiss(); + } + if (!suppressUpdate) { + this.dispatchEvent({ + type: goog.ui.ac.AutoComplete.EventType.UPDATE, + row: selectedRow, + index: index + }); + if (this.triggerSuggestionsOnUpdate_) { + this.selectionHandler_.update(true); + } + } + return true; + } else { + this.dismiss(); + this.dispatchEvent({ + type: goog.ui.ac.AutoComplete.EventType.UPDATE, + row: null, + index: null + }); + return false; + } +}; + + +/** + * Returns whether or not the autocomplete is open and has a highlighted row. + * @return {boolean} Whether an autocomplete row is highlighted. + */ +goog.ui.ac.AutoComplete.prototype.hasHighlight = function() { + return this.isOpen() && this.getIndexOfId(this.hiliteId_) != -1; +}; + + +/** + * Clears out the token, rows, and hilite, and calls + * renderer.dismiss() + */ +goog.ui.ac.AutoComplete.prototype.dismiss = function() { + this.hiliteId_ = -1; + this.token_ = null; + this.firstRowId_ += this.rows_.length; + this.rows_ = []; + window.clearTimeout(this.dismissTimer_); + this.dismissTimer_ = null; + this.renderer_.dismiss(); + this.dispatchEvent(goog.ui.ac.AutoComplete.EventType.SUGGESTIONS_UPDATE); + this.dispatchEvent(goog.ui.ac.AutoComplete.EventType.DISMISS); +}; + + +/** + * Call a dismiss after a delay, if there's already a dismiss active, ignore. + */ +goog.ui.ac.AutoComplete.prototype.dismissOnDelay = function() { + if (!this.dismissTimer_) { + this.dismissTimer_ = window.setTimeout(goog.bind(this.dismiss, this), 100); + } +}; + + +/** + * Cancels any delayed dismiss events immediately. + * @return {boolean} Whether a delayed dismiss was cancelled. + * @private + */ +goog.ui.ac.AutoComplete.prototype.immediatelyCancelDelayedDismiss_ = + function() { + if (this.dismissTimer_) { + window.clearTimeout(this.dismissTimer_); + this.dismissTimer_ = null; + return true; + } + return false; +}; + + +/** + * Cancel the active delayed dismiss if there is one. + */ +goog.ui.ac.AutoComplete.prototype.cancelDelayedDismiss = function() { + // Under certain circumstances a cancel event occurs immediately prior to a + // delayedDismiss event that it should be cancelling. To handle this situation + // properly, a timer is used to stop that event. + // Using only the timer creates undesirable behavior when the cancel occurs + // less than 10ms before the delayed dismiss timout ends. If that happens the + // clearTimeout() will occur too late and have no effect. + if (!this.immediatelyCancelDelayedDismiss_()) { + window.setTimeout( + goog.bind(this.immediatelyCancelDelayedDismiss_, this), 10); + } +}; + + +/** @override */ +goog.ui.ac.AutoComplete.prototype.disposeInternal = function() { + goog.ui.ac.AutoComplete.superClass_.disposeInternal.call(this); + delete this.inputToAnchorMap_; + this.renderer_.dispose(); + this.selectionHandler_.dispose(); + this.matcher_ = null; +}; + + +/** + * Callback passed to Matcher when requesting matches for a token. + * This might be called synchronously, or asynchronously, or both, for + * any implementation of a Matcher. + * If the Matcher calls this back, with the same token this AutoComplete + * has set currently, then this will package the matching rows in object + * of the form + *
      + * {
      + *   id: an integer ID unique to this result set and AutoComplete instance,
      + *   data: the raw row data from Matcher
      + * }
      + * 
      + * + * @param {string} matchedToken Token that corresponds with the rows. + * @param {!Array} rows Set of data that match the given token. + * @param {(boolean|goog.ui.ac.RenderOptions)=} opt_options If true, + * keeps the currently hilited (by index) element hilited. If false not. + * Otherwise a RenderOptions object. + * @private + */ +goog.ui.ac.AutoComplete.prototype.matchListener_ = function( + matchedToken, rows, opt_options) { + if (this.token_ != matchedToken) { + // Matcher's response token doesn't match current token. + // This is probably an async response that came in after + // the token was changed, so don't do anything. + return; + } + + this.renderRows(rows, opt_options); +}; + + +/** + * Renders the rows and adds highlighting. + * @param {!Array} rows Set of data that match the given token. + * @param {(boolean|goog.ui.ac.RenderOptions)=} opt_options If true, + * keeps the currently hilited (by index) element hilited. If false not. + * Otherwise a RenderOptions object. + */ +goog.ui.ac.AutoComplete.prototype.renderRows = function(rows, opt_options) { + // The optional argument should be a RenderOptions object. It can be a + // boolean for backwards compatibility, defaulting to false. + var optionsObj = goog.typeOf(opt_options) == 'object' && opt_options; + + var preserveHilited = + optionsObj ? optionsObj.getPreserveHilited() : opt_options; + var indexToHilite = preserveHilited ? this.getIndexOfId(this.hiliteId_) : -1; + + // Current token matches the matcher's response token. + this.firstRowId_ += this.rows_.length; + this.rows_ = rows; + var rendRows = []; + for (var i = 0; i < rows.length; ++i) { + rendRows.push({id: this.getIdOfIndex_(i), data: rows[i]}); + } + + var anchor = null; + if (this.target_) { + anchor = this.inputToAnchorMap_[goog.getUid(this.target_)] || this.target_; + } + this.renderer_.setAnchorElement(anchor); + this.renderer_.renderRows(rendRows, this.token_, this.target_); + + var autoHilite = this.autoHilite_; + if (optionsObj && optionsObj.getAutoHilite() !== undefined) { + autoHilite = optionsObj.getAutoHilite(); + } + this.hiliteId_ = -1; + if ((autoHilite || indexToHilite >= 0) && rendRows.length != 0 && + this.token_) { + if (indexToHilite >= 0) { + this.hiliteId(this.getIdOfIndex_(indexToHilite)); + } else { + // Hilite the first non-disabled row. + this.hiliteNext(); + } + } + this.dispatchEvent(goog.ui.ac.AutoComplete.EventType.SUGGESTIONS_UPDATE); +}; + + +/** + * Gets the index corresponding to a particular id. + * @param {number} id A unique id for the row. + * @return {number} A valid index into rows_, or -1 if the id is invalid. + * @protected + */ +goog.ui.ac.AutoComplete.prototype.getIndexOfId = function(id) { + var index = id - this.firstRowId_; + if (index < 0 || index >= this.rows_.length) { + return -1; + } + return index; +}; + + +/** + * Gets the id corresponding to a particular index. (Does no checking.) + * @param {number} index The index of a row in the result set. + * @return {number} The id that currently corresponds to that index. + * @private + */ +goog.ui.ac.AutoComplete.prototype.getIdOfIndex_ = function(index) { + return this.firstRowId_ + index; +}; + + +/** + * Attach text areas or input boxes to the autocomplete by DOM reference. After + * elements are attached to the autocomplete, when a user types they will see + * the autocomplete drop down. + * @param {...Element} var_args Variable args: Input or text area elements to + * attach the autocomplete too. + */ +goog.ui.ac.AutoComplete.prototype.attachInputs = function(var_args) { + // Delegate to the input handler + var inputHandler = /** @type {goog.ui.ac.InputHandler} */ + (this.selectionHandler_); + inputHandler.attachInputs.apply(inputHandler, arguments); +}; + + +/** + * Detach text areas or input boxes to the autocomplete by DOM reference. + * @param {...Element} var_args Variable args: Input or text area elements to + * detach from the autocomplete. + */ +goog.ui.ac.AutoComplete.prototype.detachInputs = function(var_args) { + // Delegate to the input handler + var inputHandler = /** @type {goog.ui.ac.InputHandler} */ + (this.selectionHandler_); + inputHandler.detachInputs.apply(inputHandler, arguments); + + // Remove mapping from input to anchor if one exists. + goog.array.forEach(arguments, function(input) { + goog.object.remove(this.inputToAnchorMap_, goog.getUid(input)); + }, this); +}; + + +/** + * Attaches the autocompleter to a text area or text input element + * with an anchor element. The anchor element is the element the + * autocomplete box will be positioned against. + * @param {Element} inputElement The input element. May be 'textarea', + * text 'input' element, or any other element that exposes similar + * interface. + * @param {Element} anchorElement The anchor element. + */ +goog.ui.ac.AutoComplete.prototype.attachInputWithAnchor = function( + inputElement, anchorElement) { + this.inputToAnchorMap_[goog.getUid(inputElement)] = anchorElement; + this.attachInputs(inputElement); +}; + + +/** + * Forces an update of the display. + * @param {boolean=} opt_force Whether to force an update. + */ +goog.ui.ac.AutoComplete.prototype.update = function(opt_force) { + var inputHandler = /** @type {goog.ui.ac.InputHandler} */ + (this.selectionHandler_); + inputHandler.update(opt_force); +}; diff --git a/closure-library/closure/goog/ui/ac/cachingmatcher.js b/closure-library/closure/goog/ui/ac/cachingmatcher.js new file mode 100644 index 0000000000..4dee1f9369 --- /dev/null +++ b/closure-library/closure/goog/ui/ac/cachingmatcher.js @@ -0,0 +1,280 @@ +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Matcher which maintains a client-side cache on top of some + * other matcher. + * @author reinerp@google.com (Reiner Pope) + */ + + +goog.provide('goog.ui.ac.CachingMatcher'); + +goog.require('goog.array'); +goog.require('goog.async.Throttle'); +goog.require('goog.ui.ac.ArrayMatcher'); +goog.require('goog.ui.ac.RenderOptions'); + + + +/** + * A matcher which wraps another (typically slow) matcher and + * keeps a client-side cache of the results. For instance, you can use this to + * wrap a RemoteArrayMatcher to hide the latency of the underlying matcher + * having to make ajax request. + * + * Objects in the cache are deduped on their stringified forms. + * + * Note - when the user types a character, they will instantly get a set of + * local results, and then some time later, the results from the server will + * show up. + * + * @constructor + * @param {!Object} baseMatcher The underlying matcher to use. Must implement + * requestMatchingRows. + * @final + */ +goog.ui.ac.CachingMatcher = function(baseMatcher) { + /** @private {!Array}} The cache. */ + this.rows_ = []; + + /** + * Set of stringified rows, for fast deduping. Each element of this.rows_ + * is stored in rowStrings_ as (' ' + row) to ensure we avoid builtin + * properties like 'toString'. + * @private {Object} + */ + this.rowStrings_ = {}; + + /** + * Maximum number of rows in the cache. If the cache grows larger than this, + * the entire cache will be emptied. + * @private {number} + */ + this.maxCacheSize_ = 1000; + + /** @private {!Object} The underlying matcher to use. */ + this.baseMatcher_ = baseMatcher; + + /** + * Local matching function. + * @private {function(string, number, !Array): !Array} + */ + this.getMatchesForRows_ = goog.ui.ac.ArrayMatcher.getMatchesForRows; + + /** @private {number} Number of matches to request from the base matcher. */ + this.baseMatcherMaxMatches_ = 100; + + /** @private {goog.async.Throttle} */ + this.throttledTriggerBaseMatch_ = + new goog.async.Throttle(this.triggerBaseMatch_, 150, this); + + /** @private {string} */ + this.mostRecentToken_ = ''; + + /** @private {Function} */ + this.mostRecentMatchHandler_ = null; + + /** @private {number} */ + this.mostRecentMaxMatches_ = 10; + + /** + * The set of rows which we last displayed. + * + * NOTE(reinerp): The need for this is subtle. When a server result comes + * back, we don't want to suddenly change the list of results without the user + * doing anything. So we make sure to add the new server results to the end of + * the currently displayed list. + * + * We need to keep track of the last rows we displayed, because the "similar + * matcher" we use locally might otherwise reorder results. + * + * @private {Array} + */ + this.mostRecentMatches_ = []; +}; + + +/** + * Sets the number of milliseconds with which to throttle the match requests + * on the underlying matcher. + * + * Default value: 150. + * + * @param {number} throttleTime . + */ +goog.ui.ac.CachingMatcher.prototype.setThrottleTime = function(throttleTime) { + this.throttledTriggerBaseMatch_ = + new goog.async.Throttle(this.triggerBaseMatch_, throttleTime, this); +}; + + +/** + * Sets the maxMatches to use for the base matcher. If the base matcher makes + * AJAX requests, it may help to make this a large number so that the local + * cache gets populated quickly. + * + * Default value: 100. + * + * @param {number} maxMatches The value to set. + */ +goog.ui.ac.CachingMatcher.prototype.setBaseMatcherMaxMatches = function( + maxMatches) { + this.baseMatcherMaxMatches_ = maxMatches; +}; + + +/** + * Sets the maximum size of the local cache. If the local cache grows larger + * than this size, it will be emptied. + * + * Default value: 1000. + * + * @param {number} maxCacheSize . + */ +goog.ui.ac.CachingMatcher.prototype.setMaxCacheSize = function(maxCacheSize) { + this.maxCacheSize_ = maxCacheSize; +}; + + +/** + * Sets the local matcher to use. + * + * The local matcher should be a function with the same signature as + * {@link goog.ui.ac.ArrayMatcher.getMatchesForRows}, i.e. its arguments are + * searchToken, maxMatches, rowsToSearch; and it returns a list of matching + * rows. + * + * Default value: {@link goog.ui.ac.ArrayMatcher.getMatchesForRows}. + * + * @param {function(string, number, !Array): !Array} + * localMatcher + */ +goog.ui.ac.CachingMatcher.prototype.setLocalMatcher = function(localMatcher) { + this.getMatchesForRows_ = localMatcher; +}; + + +/** + * Function used to pass matches to the autocomplete. + * @param {string} token Token to match. + * @param {number} maxMatches Max number of matches to return. + * @param {Function} matchHandler callback to execute after matching. + */ +goog.ui.ac.CachingMatcher.prototype.requestMatchingRows = function( + token, maxMatches, matchHandler) { + this.mostRecentMaxMatches_ = maxMatches; + this.mostRecentToken_ = token; + this.mostRecentMatchHandler_ = matchHandler; + this.throttledTriggerBaseMatch_.fire(); + + var matches = this.getMatchesForRows_(token, maxMatches, this.rows_); + matchHandler(token, matches); + this.mostRecentMatches_ = matches; +}; + + +/** Clears the cache. */ +goog.ui.ac.CachingMatcher.prototype.clearCache = function() { + this.rows_ = []; + this.rowStrings_ = {}; +}; + + +/** + * Adds the specified rows to the cache. + * @param {!Array} rows . + * @private + */ +goog.ui.ac.CachingMatcher.prototype.addRows_ = function(rows) { + goog.array.forEach(rows, function(row) { + // The ' ' prefix is to avoid colliding with builtins like toString. + if (!this.rowStrings_[' ' + row]) { + this.rows_.push(row); + this.rowStrings_[' ' + row] = true; + } + }, this); +}; + + +/** + * Checks if the cache is larger than the maximum cache size. If so clears it. + * @private + */ +goog.ui.ac.CachingMatcher.prototype.clearCacheIfTooLarge_ = function() { + if (this.rows_.length > this.maxCacheSize_) { + this.clearCache(); + } +}; + + +/** + * Triggers a match request against the base matcher. This function is + * unthrottled, so don't call it directly; instead use + * this.throttledTriggerBaseMatch_. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.ac.CachingMatcher.prototype.triggerBaseMatch_ = function() { + this.baseMatcher_.requestMatchingRows( + this.mostRecentToken_, this.baseMatcherMaxMatches_, + goog.bind(this.onBaseMatch_, this)); +}; + + +/** + * Handles a match response from the base matcher. + * @param {string} token The token against which the base match was called. + * @param {!Array} matches The matches returned by the base matcher. + * @private + */ +goog.ui.ac.CachingMatcher.prototype.onBaseMatch_ = function(token, matches) { + // NOTE(reinerp): The user might have typed some more characters since the + // base matcher request was sent out, which manifests in that token might be + // older than this.mostRecentToken_. We make sure to do our local matches + // using this.mostRecentToken_ rather than token so that we display results + // relevant to what the user is seeing right now. + + // NOTE(reinerp): We compute a diff between the currently displayed results + // and the new results we would get now that the server results have come + // back. Using this diff, we make sure the new results are only added to the + // end of the list of results. See the documentation on + // this.mostRecentMatches_ for details + + this.addRows_(matches); + + var oldMatchesSet = {}; + goog.array.forEach(this.mostRecentMatches_, function(match) { + // The ' ' prefix is to avoid colliding with builtins like toString. + oldMatchesSet[' ' + match] = true; + }); + var newMatches = this.getMatchesForRows_( + this.mostRecentToken_, this.mostRecentMaxMatches_, this.rows_); + newMatches = goog.array.filter( + newMatches, function(match) { return !(oldMatchesSet[' ' + match]); }); + newMatches = this.mostRecentMatches_.concat(newMatches) + .slice(0, this.mostRecentMaxMatches_); + + this.mostRecentMatches_ = newMatches; + + // We've gone to the effort of keeping the existing rows as before, so let's + // make sure to keep them highlighted. + var options = new goog.ui.ac.RenderOptions(); + options.setPreserveHilited(true); + this.mostRecentMatchHandler_(this.mostRecentToken_, newMatches, options); + + // We clear the cache *after* running the local match, so we don't + // suddenly remove results just because the remote match came back. + this.clearCacheIfTooLarge_(); +}; diff --git a/closure-library/closure/goog/ui/ac/inputhandler.js b/closure-library/closure/goog/ui/ac/inputhandler.js new file mode 100644 index 0000000000..41a5a4e33f --- /dev/null +++ b/closure-library/closure/goog/ui/ac/inputhandler.js @@ -0,0 +1,1351 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Class for managing the interactions between an + * auto-complete object and a text-input or textarea. + * + * IME note: + * + * We used to suspend autocomplete while there are IME preedit characters, but + * now for parity with Search we do not. We still detect the beginning and end + * of IME entry because we need to listen to more events while an IME commit is + * happening, but we update continuously as the user types. + * + * IMEs vary across operating systems, browsers, and even input languages. This + * class tries to handle IME for: + * - Windows x {FF3, IE7, Chrome} x MS IME 2002 (Japanese) + * - Mac x {FF3, Safari3} x Kotoeri (Japanese) + * - Linux x {FF3} x UIM + Anthy (Japanese) + * + * TODO(user): We cannot handle {Mac, Linux} x FF3 correctly. + * TODO(user): We need to support Windows x Google IME. + * + * This class was tested with hiragana input. The event sequence when inputting + * 'ai' with IME on (which commits two characters) is as follows: + * + * Notation: [key down code, key press, key up code] + * key code or +: event fired + * -: event not fired + * + * - Win/FF3: [WIN_IME, +, A], [-, -, ENTER] + * Note: No events are fired for 'i'. + * + * - Win/IE7: [WIN_IME, -, A], [WIN_IME, -, I], [WIN_IME, -, ENTER] + * + * - Win/Chrome: Same as Win/IE7 + * + * - Mac/FF3: [A, -, A], [I, -, I], [ENTER, -, ENTER] + * + * - Mac/Safari3: Same as Win/IE7 + * + * - Linux/FF3: No events are generated. + * + * With IME off, + * + * - ALL: [A, +, A], [I, +, I], [ENTER, +, ENTER] + * Note: Key code of key press event varies across configuration. + * + * With Microsoft Pinyin IME 3.0 (Simplified Chinese), + * + * - Win/IE7: Same as Win/IE7 with MS IME 2002 (Japanese) + * + * The issue with this IME is that the key sequence that ends preedit is not + * a single ENTER key up. + * - ENTER key up following either ENTER or SPACE ends preedit. + * - SPACE key up following even number of LEFT, RIGHT, or SPACE (any + * combination) ends preedit. + * TODO(user): We only support SPACE-then-ENTER sequence. + * TODO(mpd): With the change to autocomplete during IME, this might not be an + * issue. Remove this comment once tested. + * + * With Microsoft Korean IME 2002, + * + * - Win/IE7: Same as Win/IE7 with MS IME 2002 (Japanese), but there is no + * sequence that ends the preedit. + * + * The following is the algorithm we use to detect IME preedit: + * + * - WIN_IME key down starts predit. + * - (1) ENTER key up or (2) CTRL-M key up ends preedit. + * - Any key press not immediately following WIN_IME key down signifies that + * preedit has ended. + * + * If you need to change this algorithm, please note the OS, browser, language, + * and behavior above so that we can avoid regressions. Contact mpd or yuzo + * if you have questions or concerns. + * + */ + + +goog.provide('goog.ui.ac.InputHandler'); + +goog.require('goog.Disposable'); +goog.require('goog.Timer'); +goog.require('goog.a11y.aria'); +goog.require('goog.a11y.aria.Role'); +goog.require('goog.a11y.aria.State'); +goog.require('goog.dom'); +goog.require('goog.dom.selection'); +goog.require('goog.events.EventHandler'); +goog.require('goog.events.EventType'); +goog.require('goog.events.KeyCodes'); +goog.require('goog.events.KeyHandler'); +goog.require('goog.string'); +goog.require('goog.userAgent'); +goog.require('goog.userAgent.product'); + + + +/** + * Class for managing the interaction between an auto-complete object and a + * text-input or textarea. + * + * @param {?string=} opt_separators Separators to split multiple entries. + * If none passed, uses ',' and ';'. + * @param {?string=} opt_literals Characters used to delimit text literals. + * @param {?boolean=} opt_multi Whether to allow multiple entries + * (Default: true). + * @param {?number=} opt_throttleTime Number of milliseconds to throttle + * keyevents with (Default: 150). Use -1 to disable updates on typing. Note + * that typing the separator will update autocomplete suggestions. + * @constructor + * @extends {goog.Disposable} + */ +goog.ui.ac.InputHandler = function( + opt_separators, opt_literals, opt_multi, opt_throttleTime) { + goog.Disposable.call(this); + var throttleTime = opt_throttleTime || 150; + + /** + * Whether this input accepts multiple values + * @type {boolean} + * @private + */ + this.multi_ = opt_multi != null ? opt_multi : true; + + // Set separators depends on this.multi_ being set correctly + this.setSeparators( + opt_separators || goog.ui.ac.InputHandler.STANDARD_LIST_SEPARATORS); + + /** + * Characters that are used to delimit literal text. Separarator characters + * found within literal text are not processed as separators + * @type {string} + * @private + */ + this.literals_ = opt_literals || ''; + + /** + * Whether to prevent highlighted item selection when tab is pressed. + * @type {boolean} + * @private + */ + this.preventSelectionOnTab_ = false; + + /** + * Whether to prevent the default behavior (moving focus to another element) + * when tab is pressed. This occurs by default only for multi-value mode. + * @type {boolean} + * @private + */ + this.preventDefaultOnTab_ = this.multi_; + + /** + * A timer object used to monitor for changes when an element is active. + * + * TODO(user): Consider tuning the throttle time, so that it takes into + * account the length of the token. When the token is short it is likely to + * match lots of rows, therefore we want to check less frequently. Even + * something as simple as <3-chars = 150ms, then 100ms otherwise. + * + * @type {goog.Timer} + * @private + */ + this.timer_ = throttleTime > 0 ? new goog.Timer(throttleTime) : null; + + /** + * Event handler used by the input handler to manage events. + * @type {goog.events.EventHandler} + * @private + */ + this.eh_ = new goog.events.EventHandler(this); + + /** + * Event handler to help us find an input element that already has the focus. + * @type {goog.events.EventHandler} + * @private + */ + this.activateHandler_ = new goog.events.EventHandler(this); + + /** + * The keyhandler used for listening on most key events. This takes care of + * abstracting away some of the browser differences. + * @type {goog.events.KeyHandler} + * @private + */ + this.keyHandler_ = new goog.events.KeyHandler(); + + /** + * The last key down key code. + * @type {number} + * @private + */ + this.lastKeyCode_ = -1; // Initialize to a non-existent value. +}; +goog.inherits(goog.ui.ac.InputHandler, goog.Disposable); +goog.tagUnsealableClass(goog.ui.ac.InputHandler); + + +/** + * Whether or not we need to pause the execution of the blur handler in order + * to allow the execution of the selection handler to run first. This is + * currently true when running on IOS version prior to 4.2, since we need + * some special logic for these devices to handle bug 4484488. + * @type {boolean} + * @private + */ +goog.ui.ac.InputHandler.REQUIRES_ASYNC_BLUR_ = + (goog.userAgent.product.IPHONE || goog.userAgent.product.IPAD) && + // Check the webkit version against the version for iOS 4.2.1. + !goog.userAgent.isVersionOrHigher('533.17.9'); + + +/** + * Standard list separators. + * @type {string} + * @const + */ +goog.ui.ac.InputHandler.STANDARD_LIST_SEPARATORS = ',;'; + + +/** + * Literals for quotes. + * @type {string} + * @const + */ +goog.ui.ac.InputHandler.QUOTE_LITERALS = '"'; + + +/** + * The AutoComplete instance this inputhandler is associated with. + * @type {goog.ui.ac.AutoComplete} + */ +goog.ui.ac.InputHandler.prototype.ac_; + + +/** + * Characters that can be used to split multiple entries in an input string + * @type {string} + * @private + */ +goog.ui.ac.InputHandler.prototype.separators_; + + +/** + * The separator we use to reconstruct the string + * @type {string} + * @private + */ +goog.ui.ac.InputHandler.prototype.defaultSeparator_; + + +/** + * Regular expression used from trimming tokens or null for no trimming. + * @type {?RegExp} + * @private + */ +goog.ui.ac.InputHandler.prototype.trimmer_; + + +/** + * Regular expression to test whether a separator exists + * @type {?RegExp} + * @private + */ +goog.ui.ac.InputHandler.prototype.separatorCheck_; + + +/** + * Should auto-completed tokens be wrapped in whitespace? Used in selectRow. + * @type {boolean} + * @private + */ +goog.ui.ac.InputHandler.prototype.whitespaceWrapEntries_ = true; + + +/** + * Should the occurrence of a literal indicate a token boundary? + * @type {boolean} + * @private + */ +goog.ui.ac.InputHandler.prototype.generateNewTokenOnLiteral_ = true; + + +/** + * Whether to flip the orientation of up & down for hiliting next + * and previous autocomplete entries. + * @type {boolean} + * @private + */ +goog.ui.ac.InputHandler.prototype.upsideDown_ = false; + + +/** + * If we're in 'multi' mode, does typing a separator force the updating of + * suggestions? + * For example, if somebody finishes typing "obama, hillary,", should the last + * comma trigger updating suggestions in a guaranteed manner? Especially useful + * when the suggestions depend on complete keywords. Note that "obama, hill" + * (a leading sub-string of "obama, hillary" will lead to different and possibly + * irrelevant suggestions. + * @type {boolean} + * @private + */ +goog.ui.ac.InputHandler.prototype.separatorUpdates_ = true; + + +/** + * If we're in 'multi' mode, does typing a separator force the current term to + * autocomplete? + * For example, if 'tomato' is a suggested completion and the user has typed + * 'to,', do we autocomplete to turn that into 'tomato,'? + * @type {boolean} + * @private + */ +goog.ui.ac.InputHandler.prototype.separatorSelects_ = true; + + +/** + * The id of the currently active timeout, so it can be cleared if required. + * @type {?number} + * @private + */ +goog.ui.ac.InputHandler.prototype.activeTimeoutId_ = null; + + +/** + * The element that is currently active. + * @type {?Element} + * @private + */ +goog.ui.ac.InputHandler.prototype.activeElement_ = null; + + +/** + * The previous value of the active element. + * @type {string} + * @private + */ +goog.ui.ac.InputHandler.prototype.lastValue_ = ''; + + +/** + * Flag used to indicate that the IME key has been seen and we need to wait for + * the up event. + * @type {boolean} + * @private + */ +goog.ui.ac.InputHandler.prototype.waitingForIme_ = false; + + +/** + * Flag used to indicate that the user just selected a row and we should + * therefore ignore the change of the input value. + * @type {boolean} + * @private + */ +goog.ui.ac.InputHandler.prototype.rowJustSelected_ = false; + + +/** + * Flag indicating whether the result list should be updated continuously + * during typing or only after a short pause. + * @type {boolean} + * @private + */ +goog.ui.ac.InputHandler.prototype.updateDuringTyping_ = true; + + +/** + * Attach an instance of an AutoComplete + * @param {goog.ui.ac.AutoComplete} ac Autocomplete object. + */ +goog.ui.ac.InputHandler.prototype.attachAutoComplete = function(ac) { + this.ac_ = ac; +}; + + +/** + * Returns the associated autocomplete instance. + * @return {goog.ui.ac.AutoComplete} The associated autocomplete instance. + */ +goog.ui.ac.InputHandler.prototype.getAutoComplete = function() { + return this.ac_; +}; + + +/** + * Returns the current active element. + * @return {Element} The currently active element. + */ +goog.ui.ac.InputHandler.prototype.getActiveElement = function() { + return this.activeElement_; +}; + + +/** + * Returns the value of the current active element. + * @return {string} The value of the current active element. + */ +goog.ui.ac.InputHandler.prototype.getValue = function() { + return this.activeElement_.value; +}; + + +/** + * Sets the value of the current active element. + * @param {string} value The new value. + */ +goog.ui.ac.InputHandler.prototype.setValue = function(value) { + this.activeElement_.value = value; +}; + + +/** + * Returns the current cursor position. + * @return {number} The index of the cursor position. + */ +goog.ui.ac.InputHandler.prototype.getCursorPosition = function() { + return goog.dom.selection.getStart(this.activeElement_); +}; + + +/** + * Sets the cursor at the given position. + * @param {number} pos The index of the cursor position. + */ +goog.ui.ac.InputHandler.prototype.setCursorPosition = function(pos) { + goog.dom.selection.setStart(this.activeElement_, pos); + goog.dom.selection.setEnd(this.activeElement_, pos); +}; + + +/** + * Attaches the input handler to a target element. The target element + * should be a textarea, input box, or other focusable element with the + * same interface. + * @param {Element|goog.events.EventTarget} target An element to attach the + * input handler to. + */ +goog.ui.ac.InputHandler.prototype.attachInput = function(target) { + if (goog.dom.isElement(target)) { + var el = /** @type {!Element} */ (target); + goog.a11y.aria.setRole(el, goog.a11y.aria.Role.COMBOBOX); + goog.a11y.aria.setState(el, goog.a11y.aria.State.AUTOCOMPLETE, 'list'); + } + + this.eh_.listen(target, goog.events.EventType.FOCUS, this.handleFocus); + this.eh_.listen(target, goog.events.EventType.BLUR, this.handleBlur); + + if (!this.activeElement_) { + this.activateHandler_.listen( + target, goog.events.EventType.KEYDOWN, + this.onKeyDownOnInactiveElement_); + + // Don't wait for a focus event if the element already has focus. + if (goog.dom.isElement(target)) { + var ownerDocument = goog.dom.getOwnerDocument( + /** @type {Element} */ (target)); + if (goog.dom.getActiveElement(ownerDocument) == target) { + this.processFocus(/** @type {!Element} */ (target)); + } + } + } +}; + + +/** + * Detaches the input handler from the provided element. + * @param {Element|goog.events.EventTarget} target An element to detach the + * input handler from. + */ +goog.ui.ac.InputHandler.prototype.detachInput = function(target) { + if (goog.dom.isElement(target)) { + var el = /** @type {!Element} */ (target); + goog.a11y.aria.removeRole(el); + goog.a11y.aria.removeState(el, goog.a11y.aria.State.AUTOCOMPLETE); + } + + if (target == this.activeElement_) { + this.handleBlur(); + } + this.eh_.unlisten(target, goog.events.EventType.FOCUS, this.handleFocus); + this.eh_.unlisten(target, goog.events.EventType.BLUR, this.handleBlur); + + if (!this.activeElement_) { + this.activateHandler_.unlisten( + target, goog.events.EventType.KEYDOWN, + this.onKeyDownOnInactiveElement_); + } +}; + + +/** + * Attaches the input handler to multiple elements. + * @param {...Element} var_args Elements to attach the input handler too. + */ +goog.ui.ac.InputHandler.prototype.attachInputs = function(var_args) { + for (var i = 0; i < arguments.length; i++) { + this.attachInput(arguments[i]); + } +}; + + +/** + * Detaches the input handler from multuple elements. + * @param {...Element} var_args Variable arguments for elements to unbind from. + */ +goog.ui.ac.InputHandler.prototype.detachInputs = function(var_args) { + for (var i = 0; i < arguments.length; i++) { + this.detachInput(arguments[i]); + } +}; + + +/** + * Selects the given row. Implements the SelectionHandler interface. + * @param {?} row The row to select. + * @param {boolean=} opt_multi Should this be treated as a single or multi-token + * auto-complete? Overrides previous setting of opt_multi on constructor. + * @return {boolean} Whether to suppress the update event. + */ +goog.ui.ac.InputHandler.prototype.selectRow = function(row, opt_multi) { + if (this.activeElement_) { + this.setTokenText(row.toString(), opt_multi); + } + return false; +}; + + +/** + * Sets the text of the current token without updating the autocomplete + * choices. + * @param {string} tokenText The text for the current token. + * @param {boolean=} opt_multi Should this be treated as a single or multi-token + * auto-complete? Overrides previous setting of opt_multi on constructor. + * @protected + */ +goog.ui.ac.InputHandler.prototype.setTokenText = function( + tokenText, opt_multi) { + if (goog.isDef(opt_multi) ? opt_multi : this.multi_) { + var index = this.getTokenIndex_(this.getValue(), this.getCursorPosition()); + + // Break up the current input string. + var entries = this.splitInput_(this.getValue()); + + // Get the new value, ignoring whitespace associated with the entry. + var replaceValue = tokenText; + + // Only add punctuation if there isn't already a separator available. + if (this.separatorCheck_ && !this.separatorCheck_.test(replaceValue)) { + replaceValue = + goog.string.trimRight(replaceValue) + this.defaultSeparator_; + } + + // Ensure there's whitespace wrapping the entries, if whitespaceWrapEntries_ + // has been set to true. + if (this.whitespaceWrapEntries_) { + if (index != 0 && !goog.string.isEmptyOrWhitespace(entries[index - 1])) { + replaceValue = ' ' + replaceValue; + } + // Add a space only if it's the last token; otherwise, we assume the + // next token already has the proper spacing. + if (index == entries.length - 1) { + replaceValue = replaceValue + ' '; + } + } + + // If the token needs changing, then update the input box and move the + // cursor to the correct position. + if (replaceValue != entries[index]) { + // Replace the value in the array. + entries[index] = replaceValue; + + var el = this.activeElement_; + // If there is an uncommitted IME in Firefox or IE 9, setting the value + // fails and results in actually clearing the value that's already in the + // input. + // The FF bug is http://bugzilla.mozilla.org/show_bug.cgi?id=549674 + // Blurring before setting the value works around this problem. We'd like + // to do this only if there is an uncommitted IME, but this isn't possible + // to detect. Since text editing is finicky we restrict this + // workaround to Firefox and IE 9 where it's necessary. + // (Note: this has been fixed in Edge and since FF 41) + if (goog.userAgent.GECKO || + (goog.userAgent.IE && goog.userAgent.isVersionOrHigher('9'))) { + el.blur(); + } + // Join the array and replace the contents of the input. + el.value = entries.join(''); + + // Calculate which position to put the cursor at. + var pos = 0; + for (var i = 0; i <= index; i++) { + pos += entries[i].length; + } + + // Set the cursor. + el.focus(); + this.setCursorPosition(pos); + } + } else { + this.setValue(tokenText); + } + + // Avoid triggering an autocomplete just because the value changed. + this.rowJustSelected_ = true; +}; + + +/** @override */ +goog.ui.ac.InputHandler.prototype.disposeInternal = function() { + goog.ui.ac.InputHandler.superClass_.disposeInternal.call(this); + if (this.activeTimeoutId_ != null) { + // Need to check against null explicitly because 0 is a valid value. + window.clearTimeout(this.activeTimeoutId_); + } + this.eh_.dispose(); + delete this.eh_; + this.activateHandler_.dispose(); + this.keyHandler_.dispose(); + goog.dispose(this.timer_); +}; + + +/** + * Sets the entry separator characters. + * + * @param {string} separators The separator characters to set. + * @param {string=} opt_defaultSeparators The defaultSeparator character to set. + */ +goog.ui.ac.InputHandler.prototype.setSeparators = function( + separators, opt_defaultSeparators) { + this.separators_ = separators; + this.defaultSeparator_ = goog.isDefAndNotNull(opt_defaultSeparators) ? + opt_defaultSeparators : + this.separators_.substring(0, 1); + + var wspaceExp = this.multi_ ? '[\\s' + this.separators_ + ']+' : '[\\s]+'; + + this.trimmer_ = new RegExp('^' + wspaceExp + '|' + wspaceExp + '$', 'g'); + this.separatorCheck_ = new RegExp('\\s*[' + this.separators_ + ']$'); +}; + + +/** + * Sets whether to flip the orientation of up & down for hiliting next + * and previous autocomplete entries. + * @param {boolean} upsideDown Whether the orientation is upside down. + */ +goog.ui.ac.InputHandler.prototype.setUpsideDown = function(upsideDown) { + this.upsideDown_ = upsideDown; +}; + + +/** + * Sets whether auto-completed tokens should be wrapped with whitespace. + * @param {boolean} newValue boolean value indicating whether or not + * auto-completed tokens should be wrapped with whitespace. + */ +goog.ui.ac.InputHandler.prototype.setWhitespaceWrapEntries = function( + newValue) { + this.whitespaceWrapEntries_ = newValue; +}; + + +/** + * Sets whether new tokens should be generated from literals. That is, should + * hello'world be two tokens, assuming ' is a literal? + * @param {boolean} newValue boolean value indicating whether or not + * new tokens should be generated from literals. + */ +goog.ui.ac.InputHandler.prototype.setGenerateNewTokenOnLiteral = function( + newValue) { + this.generateNewTokenOnLiteral_ = newValue; +}; + + +/** + * Sets the regular expression used to trim the tokens before passing them to + * the matcher: every substring that matches the given regular expression will + * be removed. This can also be set to null to disable trimming. + * @param {?RegExp} trimmer Regexp to use for trimming or null to disable it. + */ +goog.ui.ac.InputHandler.prototype.setTrimmingRegExp = function(trimmer) { + this.trimmer_ = trimmer; +}; + + +/** + * Sets the regular expression used to check whether the replacement (used to + * update the text area after a row is selected) ends with a separator. This can + * be set to null if the input handler should never automatically append a + * separator to the replacement string. + * @param {?RegExp} separatorCheck Regexp to use for checking whether the + * replacement ends with a separator. + */ +goog.ui.ac.InputHandler.prototype.setEndsWithSeparatorRegExp = function( + separatorCheck) { + this.separatorCheck_ = separatorCheck; +}; + + +/** + * Sets whether we will prevent the default input behavior (moving focus to the + * next focusable element) on TAB. + * @param {boolean} newValue Whether to preventDefault on TAB. + */ +goog.ui.ac.InputHandler.prototype.setPreventDefaultOnTab = function(newValue) { + this.preventDefaultOnTab_ = newValue; +}; + + +/** + * Sets whether we will prevent highlighted item selection on TAB. + * @param {boolean} newValue Whether to prevent selection on TAB. + */ +goog.ui.ac.InputHandler.prototype.setPreventSelectionOnTab = function( + newValue) { + this.preventSelectionOnTab_ = newValue; +}; + + +/** + * Sets whether separators perform autocomplete. + * @param {boolean} newValue Whether to autocomplete on separators. + */ +goog.ui.ac.InputHandler.prototype.setSeparatorCompletes = function(newValue) { + this.separatorUpdates_ = newValue; + this.separatorSelects_ = newValue; +}; + + +/** + * Sets whether separators perform autocomplete. + * @param {boolean} newValue Whether to autocomplete on separators. + */ +goog.ui.ac.InputHandler.prototype.setSeparatorSelects = function(newValue) { + this.separatorSelects_ = newValue; +}; + + +/** + * Gets the time to wait before updating the results. If the update during + * typing flag is switched on, this delay counts from the last update, + * otherwise from the last keypress. + * @return {number} Throttle time in milliseconds. + */ +goog.ui.ac.InputHandler.prototype.getThrottleTime = function() { + return this.timer_ ? this.timer_.getInterval() : -1; +}; + + +/** + * Sets whether a row has just been selected. + * @param {boolean} justSelected Whether or not the row has just been selected. + */ +goog.ui.ac.InputHandler.prototype.setRowJustSelected = function(justSelected) { + this.rowJustSelected_ = justSelected; +}; + + +/** + * Sets the time to wait before updating the results. + * @param {number} time New throttle time in milliseconds. + */ +goog.ui.ac.InputHandler.prototype.setThrottleTime = function(time) { + if (time < 0) { + this.timer_.dispose(); + this.timer_ = null; + return; + } + if (this.timer_) { + this.timer_.setInterval(time); + } else { + this.timer_ = new goog.Timer(time); + } +}; + + +/** + * Gets whether the result list is updated during typing. + * @return {boolean} Value of the flag. + */ +goog.ui.ac.InputHandler.prototype.getUpdateDuringTyping = function() { + return this.updateDuringTyping_; +}; + + +/** + * Sets whether the result list should be updated during typing. + * @param {boolean} value New value of the flag. + */ +goog.ui.ac.InputHandler.prototype.setUpdateDuringTyping = function(value) { + this.updateDuringTyping_ = value; +}; + + +/** + * Handles a key event. + * @param {goog.events.BrowserEvent} e Browser event object. + * @return {boolean} True if the key event was handled. + * @protected + */ +goog.ui.ac.InputHandler.prototype.handleKeyEvent = function(e) { + switch (e.keyCode) { + // If the menu is open and 'down' caused a change then prevent the default + // action and prevent scrolling. If the box isn't a multi autocomplete + // and the menu isn't open, we force it open now. + case goog.events.KeyCodes.DOWN: + if (this.ac_.isOpen()) { + this.moveDown_(); + e.preventDefault(); + return true; + + } else if (!this.multi_) { + this.update(true); + e.preventDefault(); + return true; + } + break; + + // If the menu is open and 'up' caused a change then prevent the default + // action and prevent scrolling. + case goog.events.KeyCodes.UP: + if (this.ac_.isOpen()) { + this.moveUp_(); + e.preventDefault(); + return true; + } + break; + + // If tab key is pressed, select the current highlighted item. The default + // action is also prevented if the input is a multi input, to prevent the + // user tabbing out of the field. + case goog.events.KeyCodes.TAB: + if (this.ac_.isOpen() && !e.shiftKey && !this.preventSelectionOnTab_) { + // Ensure the menu is up to date before completing. + this.update(); + if (this.ac_.selectHilited() && this.preventDefaultOnTab_) { + e.preventDefault(); + return true; + } + } else { + this.ac_.dismiss(); + } + break; + + // On enter, just select the highlighted row. + case goog.events.KeyCodes.ENTER: + if (this.ac_.isOpen()) { + // Ensure the menu is up to date before completing. + this.update(); + if (this.ac_.selectHilited()) { + e.preventDefault(); + e.stopPropagation(); + return true; + } + } else { + this.ac_.dismiss(); + } + break; + + // On escape tell the autocomplete to dismiss. + case goog.events.KeyCodes.ESC: + if (this.ac_.isOpen()) { + this.ac_.dismiss(); + e.preventDefault(); + e.stopPropagation(); + return true; + } + break; + + // The IME keycode indicates an IME sequence has started, we ignore all + // changes until we get an enter key-up. + case goog.events.KeyCodes.WIN_IME: + if (!this.waitingForIme_) { + this.startWaitingForIme_(); + return true; + } + break; + + default: + if (this.timer_ && !this.updateDuringTyping_) { + // Waits throttle time before sending the request again. + this.timer_.stop(); + this.timer_.start(); + } + } + + return this.handleSeparator_(e); +}; + + +/** + * Handles a key event for a separator key. + * @param {goog.events.BrowserEvent} e Browser event object. + * @return {boolean} True if the key event was handled. + * @private + */ +goog.ui.ac.InputHandler.prototype.handleSeparator_ = function(e) { + var isSeparatorKey = this.multi_ && e.charCode && + this.separators_.indexOf(String.fromCharCode(e.charCode)) != -1; + if (this.separatorUpdates_ && isSeparatorKey) { + this.update(); + } + if (this.separatorSelects_ && isSeparatorKey) { + if (this.ac_.selectHilited()) { + e.preventDefault(); + return true; + } + } + return false; +}; + + +/** + * @return {boolean} Whether this inputhandler need to listen on key-up. + * @protected + */ +goog.ui.ac.InputHandler.prototype.needKeyUpListener = function() { + return false; +}; + + +/** + * Handles the key up event. Registered only if needKeyUpListener returns true. + * @param {goog.events.Event} e The keyup event. + * @return {boolean} Whether an action was taken or not. + * @protected + */ +goog.ui.ac.InputHandler.prototype.handleKeyUp = function(e) { + return false; +}; + + +/** + * Adds the necessary input event handlers. + * @private + */ +goog.ui.ac.InputHandler.prototype.addEventHandlers_ = function() { + this.keyHandler_.attach(this.activeElement_); + this.eh_.listen( + this.keyHandler_, goog.events.KeyHandler.EventType.KEY, this.onKey_); + if (this.needKeyUpListener()) { + this.eh_.listen( + this.activeElement_, goog.events.EventType.KEYUP, this.handleKeyUp); + } + this.eh_.listen( + this.activeElement_, goog.events.EventType.MOUSEDOWN, this.onMouseDown_); + + // IE6 also needs a keypress to check if the user typed a separator + if (goog.userAgent.IE) { + this.eh_.listen( + this.activeElement_, goog.events.EventType.KEYPRESS, + this.onIeKeyPress_); + } +}; + + +/** + * Removes the necessary input event handlers. + * @private + */ +goog.ui.ac.InputHandler.prototype.removeEventHandlers_ = function() { + this.eh_.unlisten( + this.keyHandler_, goog.events.KeyHandler.EventType.KEY, this.onKey_); + this.keyHandler_.detach(); + this.eh_.unlisten( + this.activeElement_, goog.events.EventType.KEYUP, this.handleKeyUp); + this.eh_.unlisten( + this.activeElement_, goog.events.EventType.MOUSEDOWN, this.onMouseDown_); + + if (goog.userAgent.IE) { + this.eh_.unlisten( + this.activeElement_, goog.events.EventType.KEYPRESS, + this.onIeKeyPress_); + } + + if (this.waitingForIme_) { + this.stopWaitingForIme_(); + } +}; + + +/** + * Handles an element getting focus. + * @param {goog.events.Event} e Browser event object. + * @protected + */ +goog.ui.ac.InputHandler.prototype.handleFocus = function(e) { + this.processFocus(/** @type {Element} */ (e.target || null)); +}; + + +/** + * Registers handlers for the active element when it receives focus. + * @param {Element} target The element to focus. + * @protected + */ +goog.ui.ac.InputHandler.prototype.processFocus = function(target) { + this.activateHandler_.removeAll(); + + if (this.ac_) { + this.ac_.cancelDelayedDismiss(); + } + + // Double-check whether the active element has actually changed. + // This is a fix for Safari 3, which fires spurious focus events. + if (target != this.activeElement_) { + this.activeElement_ = target; + if (this.timer_) { + this.timer_.start(); + this.eh_.listen(this.timer_, goog.Timer.TICK, this.onTick_); + } + this.lastValue_ = this.getValue(); + this.addEventHandlers_(); + } +}; + + +/** + * Handles an element blurring. + * @param {goog.events.Event=} opt_e Browser event object. + * @protected + */ +goog.ui.ac.InputHandler.prototype.handleBlur = function(opt_e) { + // Phones running iOS prior to version 4.2. + if (goog.ui.ac.InputHandler.REQUIRES_ASYNC_BLUR_) { + // @bug 4484488 This is required so that the menu works correctly on + // iOS prior to version 4.2. Otherwise, the blur action closes the menu + // before the menu button click can be processed. + // In order to fix the bug, we set a timeout to process the blur event, so + // that any pending selection event can be processed first. + this.activeTimeoutId_ = + window.setTimeout(goog.bind(this.processBlur, this), 0); + return; + } else { + this.processBlur(); + } +}; + + +/** + * Helper function that does the logic to handle an element blurring. + * @protected + */ +goog.ui.ac.InputHandler.prototype.processBlur = function() { + // it's possible that a blur event could fire when there's no active element, + // in the case where attachInput was called on an input that already had + // the focus + if (this.activeElement_) { + this.removeEventHandlers_(); + this.activeElement_ = null; + + if (this.timer_) { + this.timer_.stop(); + this.eh_.unlisten(this.timer_, goog.Timer.TICK, this.onTick_); + } + + if (this.ac_) { + // Pause dismissal slightly to take into account any other events that + // might fire on the renderer (e.g. a click will lose the focus). + this.ac_.dismissOnDelay(); + } + } +}; + + +/** + * Handles the timer's tick event. Calculates the current token, and reports + * any update to the autocomplete. + * @param {goog.events.Event} e Browser event object. + * @private + */ +goog.ui.ac.InputHandler.prototype.onTick_ = function(e) { + this.update(); +}; + + +/** + * Handles typing in an inactive input element. Activate it. + * @param {goog.events.BrowserEvent} e Browser event object. + * @private + */ +goog.ui.ac.InputHandler.prototype.onKeyDownOnInactiveElement_ = function(e) { + this.handleFocus(e); +}; + + +/** + * Handles typing in the active input element. Checks if the key is a special + * key and does the relevant action as appropriate. + * @param {goog.events.BrowserEvent} e Browser event object. + * @private + */ +goog.ui.ac.InputHandler.prototype.onKey_ = function(e) { + this.lastKeyCode_ = e.keyCode; + if (this.ac_) { + this.handleKeyEvent(e); + } +}; + + +/** + * Handles a KEYPRESS event generated by typing in the active input element. + * Checks if IME input is ended. + * @param {goog.events.BrowserEvent} e Browser event object. + * @private + */ +goog.ui.ac.InputHandler.prototype.onKeyPress_ = function(e) { + if (this.waitingForIme_ && + this.lastKeyCode_ != goog.events.KeyCodes.WIN_IME) { + this.stopWaitingForIme_(); + } +}; + + +/** + * Handles the key-up event. This is only ever used by Mac FF or when we are in + * an IME entry scenario. + * @param {goog.events.BrowserEvent} e Browser event object. + * @private + */ +goog.ui.ac.InputHandler.prototype.onKeyUp_ = function(e) { + if (this.waitingForIme_ && + (e.keyCode == goog.events.KeyCodes.ENTER || + (e.keyCode == goog.events.KeyCodes.M && e.ctrlKey))) { + this.stopWaitingForIme_(); + } +}; + + +/** + * Handles mouse-down event. + * @param {goog.events.BrowserEvent} e Browser event object. + * @private + */ +goog.ui.ac.InputHandler.prototype.onMouseDown_ = function(e) { + if (this.ac_) { + this.handleMouseDown(e); + } +}; + + +/** + * For subclasses to override to handle the mouse-down event. + * @param {goog.events.BrowserEvent} e Browser event object. + * @protected + */ +goog.ui.ac.InputHandler.prototype.handleMouseDown = function(e) {}; + + +/** + * Starts waiting for IME. + * @private + */ +goog.ui.ac.InputHandler.prototype.startWaitingForIme_ = function() { + if (this.waitingForIme_) { + return; + } + this.eh_.listen( + this.activeElement_, goog.events.EventType.KEYUP, this.onKeyUp_); + this.eh_.listen( + this.activeElement_, goog.events.EventType.KEYPRESS, this.onKeyPress_); + this.waitingForIme_ = true; +}; + + +/** + * Stops waiting for IME. + * @private + */ +goog.ui.ac.InputHandler.prototype.stopWaitingForIme_ = function() { + if (!this.waitingForIme_) { + return; + } + this.waitingForIme_ = false; + this.eh_.unlisten( + this.activeElement_, goog.events.EventType.KEYPRESS, this.onKeyPress_); + this.eh_.unlisten( + this.activeElement_, goog.events.EventType.KEYUP, this.onKeyUp_); +}; + + +/** + * Handles the key-press event for IE, checking to see if the user typed a + * separator character. + * @param {goog.events.BrowserEvent} e Browser event object. + * @private + */ +goog.ui.ac.InputHandler.prototype.onIeKeyPress_ = function(e) { + this.handleSeparator_(e); +}; + + +/** + * Checks if an update has occurred and notified the autocomplete of the new + * token. + * @param {boolean=} opt_force If true the menu will be forced to update. + */ +goog.ui.ac.InputHandler.prototype.update = function(opt_force) { + if (this.activeElement_ && + (opt_force || this.getValue() != this.lastValue_)) { + if (opt_force || !this.rowJustSelected_) { + var token = this.parseToken(); + + if (this.ac_) { + this.ac_.setTarget(this.activeElement_); + this.ac_.setToken(token, this.getValue()); + } + } + this.lastValue_ = this.getValue(); + } + this.rowJustSelected_ = false; +}; + + +/** + * Parses a text area or input box for the currently highlighted token. + * @return {string} Token to complete. + * @protected + */ +goog.ui.ac.InputHandler.prototype.parseToken = function() { + return this.parseToken_(); +}; + + +/** + * Moves hilite up. May hilite next or previous depending on orientation. + * @return {boolean} True if successful. + * @private + */ +goog.ui.ac.InputHandler.prototype.moveUp_ = function() { + return this.upsideDown_ ? this.ac_.hiliteNext() : this.ac_.hilitePrev(); +}; + + +/** + * Moves hilite down. May hilite next or previous depending on orientation. + * @return {boolean} True if successful. + * @private + */ +goog.ui.ac.InputHandler.prototype.moveDown_ = function() { + return this.upsideDown_ ? this.ac_.hilitePrev() : this.ac_.hiliteNext(); +}; + + +/** + * Parses a text area or input box for the currently highlighted token. + * @return {string} Token to complete. + * @private + */ +goog.ui.ac.InputHandler.prototype.parseToken_ = function() { + var caret = this.getCursorPosition(); + var text = this.getValue(); + return this.trim_(this.splitInput_(text)[this.getTokenIndex_(text, caret)]); +}; + + +/** + * Trims a token of characters that we want to ignore + * @param {string} text string to trim. + * @return {string} Trimmed string. + * @private + */ +goog.ui.ac.InputHandler.prototype.trim_ = function(text) { + return this.trimmer_ ? String(text).replace(this.trimmer_, '') : text; +}; + + +/** + * Gets the index of the currently highlighted token + * @param {string} text string to parse. + * @param {number} caret Position of cursor in string. + * @return {number} Index of token. + * @private + */ +goog.ui.ac.InputHandler.prototype.getTokenIndex_ = function(text, caret) { + // Split up the input string into multiple entries + var entries = this.splitInput_(text); + + // Short-circuit to select the last entry + if (caret == text.length) return entries.length - 1; + + // Calculate which of the entries the cursor is currently in + var current = 0; + for (var i = 0, pos = 0; i < entries.length && pos <= caret; i++) { + pos += entries[i].length; + current = i; + } + + // Get the token for the current item + return current; +}; + + +/** + * Splits an input string of text at the occurrence of a character in + * {@link goog.ui.ac.InputHandler.prototype.separators_} and creates + * an array of tokens. Each token may contain additional whitespace and + * formatting marks. If necessary use + * {@link goog.ui.ac.InputHandler.prototype.trim_} to clean up the + * entries. + * + * @param {string} text Input text. + * @return {!Array} Parsed array. + * @private + */ +goog.ui.ac.InputHandler.prototype.splitInput_ = function(text) { + if (!this.multi_) { + return [text]; + } + + var arr = String(text).split(''); + var parts = []; + var cache = []; + + for (var i = 0, inLiteral = false; i < arr.length; i++) { + if (this.literals_ && this.literals_.indexOf(arr[i]) != -1) { + if (this.generateNewTokenOnLiteral_ && !inLiteral) { + parts.push(cache.join('')); + cache.length = 0; + } + cache.push(arr[i]); + inLiteral = !inLiteral; + + } else if (!inLiteral && this.separators_.indexOf(arr[i]) != -1) { + cache.push(arr[i]); + parts.push(cache.join('')); + cache.length = 0; + + } else { + cache.push(arr[i]); + } + } + parts.push(cache.join('')); + + return parts; +}; diff --git a/closure-library/closure/goog/ui/ac/remote.js b/closure-library/closure/goog/ui/ac/remote.js new file mode 100644 index 0000000000..6f1152527d --- /dev/null +++ b/closure-library/closure/goog/ui/ac/remote.js @@ -0,0 +1,113 @@ +// Copyright 2007 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 Factory class to create a simple autocomplete that will match + * from an array of data provided via ajax. + * + * @see ../../demos/autocompleteremote.html + */ + +goog.provide('goog.ui.ac.Remote'); + +goog.require('goog.ui.ac.AutoComplete'); +goog.require('goog.ui.ac.InputHandler'); +goog.require('goog.ui.ac.RemoteArrayMatcher'); +goog.require('goog.ui.ac.Renderer'); + + + +/** + * Factory class for building a remote autocomplete widget that autocompletes + * an inputbox or text area from a data array provided via ajax. + * @param {string} url The Uri which generates the auto complete matches. + * @param {Element} input Input element or text area. + * @param {boolean=} opt_multi Whether to allow multiple entries; defaults + * to false. + * @param {boolean=} opt_useSimilar Whether to use similar matches; e.g. + * "gost" => "ghost". + * @constructor + * @extends {goog.ui.ac.AutoComplete} + */ +goog.ui.ac.Remote = function(url, input, opt_multi, opt_useSimilar) { + var matcher = new goog.ui.ac.RemoteArrayMatcher(url, !opt_useSimilar); + this.matcher_ = matcher; + + var renderer = new goog.ui.ac.Renderer(); + + var inputhandler = new goog.ui.ac.InputHandler(null, null, !!opt_multi, 300); + + goog.ui.ac.AutoComplete.call(this, matcher, renderer, inputhandler); + + inputhandler.attachAutoComplete(this); + inputhandler.attachInputs(input); +}; +goog.inherits(goog.ui.ac.Remote, goog.ui.ac.AutoComplete); + + +/** + * Set whether or not standard highlighting should be used when rendering rows. + * @param {boolean} useStandardHighlighting true if standard highlighting used. + */ +goog.ui.ac.Remote.prototype.setUseStandardHighlighting = function( + useStandardHighlighting) { + this.renderer_.setUseStandardHighlighting(useStandardHighlighting); +}; + + +/** + * Gets the attached InputHandler object. + * @return {goog.ui.ac.InputHandler} The input handler. + */ +goog.ui.ac.Remote.prototype.getInputHandler = function() { + return /** @type {goog.ui.ac.InputHandler} */ (this.selectionHandler_); +}; + + +/** + * Set the send method ("GET", "POST") for the matcher. + * @param {string} method The send method; default: GET. + */ +goog.ui.ac.Remote.prototype.setMethod = function(method) { + this.matcher_.setMethod(method); +}; + + +/** + * Set the post data for the matcher. + * @param {string} content Post data. + */ +goog.ui.ac.Remote.prototype.setContent = function(content) { + this.matcher_.setContent(content); +}; + + +/** + * Set the HTTP headers for the matcher. + * @param {Object|goog.structs.Map} headers Map of headers to add to the + * request. + */ +goog.ui.ac.Remote.prototype.setHeaders = function(headers) { + this.matcher_.setHeaders(headers); +}; + + +/** + * Set the timeout interval for the matcher. + * @param {number} interval Number of milliseconds after which an + * incomplete request will be aborted; 0 means no timeout is set. + */ +goog.ui.ac.Remote.prototype.setTimeoutInterval = function(interval) { + this.matcher_.setTimeoutInterval(interval); +}; diff --git a/closure-library/closure/goog/ui/ac/remotearraymatcher.js b/closure-library/closure/goog/ui/ac/remotearraymatcher.js new file mode 100644 index 0000000000..2854579699 --- /dev/null +++ b/closure-library/closure/goog/ui/ac/remotearraymatcher.js @@ -0,0 +1,273 @@ +// Copyright 2007 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 Class that retrieves autocomplete matches via an ajax call. + * + */ + +goog.provide('goog.ui.ac.RemoteArrayMatcher'); + +goog.require('goog.Disposable'); +goog.require('goog.Uri'); +goog.require('goog.events'); +goog.require('goog.net.EventType'); +goog.require('goog.net.XhrIo'); + + + +/** + * An array matcher that requests matches via ajax. + * @param {string} url The Uri which generates the auto complete matches. The + * search term is passed to the server as the 'token' query param. + * @param {boolean=} opt_noSimilar If true, request that the server does not do + * similarity matches for the input token against the dictionary. + * The value is sent to the server as the 'use_similar' query param which is + * either "1" (opt_noSimilar==false) or "0" (opt_noSimilar==true). + * @param {goog.net.XmlHttpFactory=} opt_xmlHttpFactory Specify the + * XmlHttpFactory used to retrieve the matches. + * @constructor + * @extends {goog.Disposable} + */ +goog.ui.ac.RemoteArrayMatcher = function( + url, opt_noSimilar, opt_xmlHttpFactory) { + goog.Disposable.call(this); + + /** + * The base URL for the ajax call. The token and max_matches are added as + * query params. + * @type {string} + * @private + */ + this.url_ = url; + + /** + * Whether similar matches should be found as well. This is sent as a hint + * to the server only. + * @type {boolean} + * @private + */ + this.useSimilar_ = !opt_noSimilar; + + /** + * The XhrIo object used for making remote requests. When a new request + * is made, the current one is aborted and the new one sent. + * @type {goog.net.XhrIo} + * @private + */ + this.xhr_ = new goog.net.XhrIo(opt_xmlHttpFactory); +}; +goog.inherits(goog.ui.ac.RemoteArrayMatcher, goog.Disposable); + + +/** + * The HTTP send method (GET, POST) to use when making the ajax call. + * @type {string} + * @private + */ +goog.ui.ac.RemoteArrayMatcher.prototype.method_ = 'GET'; + + +/** + * Data to submit during a POST. + * @type {string|undefined} + * @private + */ +goog.ui.ac.RemoteArrayMatcher.prototype.content_ = undefined; + + +/** + * Headers to send with every HTTP request. + * @type {Object|goog.structs.Map} + * @private + */ +goog.ui.ac.RemoteArrayMatcher.prototype.headers_ = null; + + +/** + * Key to the listener on XHR. Used to clear previous listeners. + * @type {goog.events.Key} + * @private + */ +goog.ui.ac.RemoteArrayMatcher.prototype.lastListenerKey_ = null; + + +/** + * Set the send method ("GET", "POST"). + * @param {string} method The send method; default: GET. + */ +goog.ui.ac.RemoteArrayMatcher.prototype.setMethod = function(method) { + this.method_ = method; +}; + + +/** + * Set the post data. + * @param {string} content Post data. + */ +goog.ui.ac.RemoteArrayMatcher.prototype.setContent = function(content) { + this.content_ = content; +}; + + +/** + * Set the HTTP headers. + * @param {Object|goog.structs.Map} headers Map of headers to add to the + * request. + */ +goog.ui.ac.RemoteArrayMatcher.prototype.setHeaders = function(headers) { + this.headers_ = headers; +}; + + +/** + * Set the timeout interval. + * @param {number} interval Number of milliseconds after which an + * incomplete request will be aborted; 0 means no timeout is set. + */ +goog.ui.ac.RemoteArrayMatcher.prototype.setTimeoutInterval = function( + interval) { + this.xhr_.setTimeoutInterval(interval); +}; + + +/** + * Builds a complete GET-style URL, given the base URI and autocomplete related + * parameter values. + * Override this to build any customized lookup URLs. + * Can be used to change request method and any post content as well. + * @param {string} uri The base URI of the request target. + * @param {string} token Current token in autocomplete. + * @param {number} maxMatches Maximum number of matches required. + * @param {boolean} useSimilar A hint to the server. + * @param {string=} opt_fullString Complete text in the input element. + * @return {?string} The complete url. Return null if no request should be sent. + * @protected + */ +goog.ui.ac.RemoteArrayMatcher.prototype.buildUrl = function( + uri, token, maxMatches, useSimilar, opt_fullString) { + var url = new goog.Uri(uri); + url.setParameterValue('token', token); + url.setParameterValue('max_matches', String(maxMatches)); + url.setParameterValue('use_similar', String(Number(useSimilar))); + return url.toString(); +}; + + +/** + * Returns whether the suggestions should be updated? + * Override this to prevent updates eg - when token is empty. + * @param {string} uri The base URI of the request target. + * @param {string} token Current token in autocomplete. + * @param {number} maxMatches Maximum number of matches required. + * @param {boolean} useSimilar A hint to the server. + * @param {string=} opt_fullString Complete text in the input element. + * @return {boolean} Whether new matches be requested. + * @protected + */ +goog.ui.ac.RemoteArrayMatcher.prototype.shouldRequestMatches = function( + uri, token, maxMatches, useSimilar, opt_fullString) { + return true; +}; + + +/** + * Parses and retrieves the array of suggestions from XHR response. + * Override this if the response is not a simple JSON array. + * @param {string} responseText The XHR response text. + * @return {Array} The array of suggestions. + * @protected + */ +goog.ui.ac.RemoteArrayMatcher.prototype.parseResponseText = function( + responseText) { + + var matches = []; + // If there is no response text, JSON.parse will throw a syntax error. + if (responseText) { + + try { + matches = JSON.parse(responseText); + } catch (exception) { + } + } + return /** @type {Array} */ (matches); +}; + + +/** + * Handles the XHR response. + * @param {string} token The XHR autocomplete token. + * @param {Function} matchHandler The AutoComplete match handler. + * @param {goog.events.Event} event The XHR success event. + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.ac.RemoteArrayMatcher.prototype.xhrCallback = function( + token, matchHandler, event) { + var text = event.target.getResponseText(); + matchHandler(token, this.parseResponseText(text)); +}; + + +/** + * Retrieve a set of matching rows from the server via ajax. + * @param {string} token The text that should be matched; passed to the server + * as the 'token' query param. + * @param {number} maxMatches The maximum number of matches requested from the + * server; passed as the 'max_matches' query param. The server is + * responsible for limiting the number of matches that are returned. + * @param {Function} matchHandler Callback to execute on the result after + * matching. + * @param {string=} opt_fullString The full string from the input box. + */ +goog.ui.ac.RemoteArrayMatcher.prototype.requestMatchingRows = function( + token, maxMatches, matchHandler, opt_fullString) { + + if (!this.shouldRequestMatches( + this.url_, token, maxMatches, this.useSimilar_, opt_fullString)) { + return; + } + // Set the query params on the URL. + var url = this.buildUrl( + this.url_, token, maxMatches, this.useSimilar_, opt_fullString); + if (!url) { + // Do nothing if there is no URL. + return; + } + + // The callback evals the server response and calls the match handler on + // the array of matches. + var callback = goog.bind(this.xhrCallback, this, token, matchHandler); + + // Abort the current request and issue the new one; prevent requests from + // being queued up by the browser with a slow server + if (this.xhr_.isActive()) { + this.xhr_.abort(); + } + // This ensures if previous XHR is aborted or ends with error, the + // corresponding success-callbacks are cleared. + if (this.lastListenerKey_) { + goog.events.unlistenByKey(this.lastListenerKey_); + } + // Listen once ensures successful callback gets cleared by itself. + this.lastListenerKey_ = + goog.events.listenOnce(this.xhr_, goog.net.EventType.SUCCESS, callback); + this.xhr_.send(url, this.method_, this.content_, this.headers_); +}; + + +/** @override */ +goog.ui.ac.RemoteArrayMatcher.prototype.disposeInternal = function() { + this.xhr_.dispose(); + goog.ui.ac.RemoteArrayMatcher.superClass_.disposeInternal.call(this); +}; diff --git a/closure-library/closure/goog/ui/ac/renderer.js b/closure-library/closure/goog/ui/ac/renderer.js new file mode 100644 index 0000000000..6e1f6a9922 --- /dev/null +++ b/closure-library/closure/goog/ui/ac/renderer.js @@ -0,0 +1,1153 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Class for rendering the results of an auto complete and + * allow the user to select an row. + * + */ + +goog.provide('goog.ui.ac.Renderer'); +goog.provide('goog.ui.ac.Renderer.CustomRenderer'); + +goog.require('goog.a11y.aria'); +goog.require('goog.a11y.aria.Role'); +goog.require('goog.a11y.aria.State'); +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.dispose'); +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.classlist'); +goog.require('goog.events'); +goog.require('goog.events.EventTarget'); +goog.require('goog.events.EventType'); +goog.require('goog.fx.dom.FadeInAndShow'); +goog.require('goog.fx.dom.FadeOutAndHide'); +goog.require('goog.positioning'); +goog.require('goog.positioning.Corner'); +goog.require('goog.positioning.Overflow'); +goog.require('goog.string'); +goog.require('goog.style'); +goog.require('goog.ui.IdGenerator'); +goog.require('goog.ui.ac.AutoComplete'); + + + +/** + * Class for rendering the results of an auto-complete in a drop down list. + * + * @constructor + * @param {Element=} opt_parentNode optional reference to the parent element + * that will hold the autocomplete elements. goog.dom.getDocument().body + * will be used if this is null. + * @param {?({renderRow}|{render})=} opt_customRenderer Custom full renderer to + * render each row. Should be something with a renderRow or render method. + * @param {boolean=} opt_rightAlign Determines if the autocomplete will always + * be right aligned. False by default. + * @param {boolean=} opt_useStandardHighlighting Determines if standard + * highlighting should be applied to each row of data. Standard highlighting + * bolds every matching substring for a given token in each row. True by + * default. + * @extends {goog.events.EventTarget} + * @suppress {underscore} + */ +goog.ui.ac.Renderer = function( + opt_parentNode, opt_customRenderer, opt_rightAlign, + opt_useStandardHighlighting) { + goog.ui.ac.Renderer.base(this, 'constructor'); + + /** + * Reference to the parent element that will hold the autocomplete elements + * @type {Element} + * @private + */ + this.parent_ = opt_parentNode || goog.dom.getDocument().body; + + /** + * Dom helper for the parent element's document. + * @type {goog.dom.DomHelper} + * @private + */ + this.dom_ = goog.dom.getDomHelper(this.parent_); + + /** + * Whether to reposition the autocomplete UI below the target node + * @type {boolean} + * @private + */ + this.reposition_ = !opt_parentNode; + + /** + * Reference to the main element that controls the rendered autocomplete + * @type {?Element} + * @private + */ + this.element_ = null; + + /** + * The current token that has been entered + * @type {string} + * @private + */ + this.token_ = ''; + + /** + * Array used to store the current set of rows being displayed + * @type {Array} + * @private + */ + this.rows_ = []; + + /** + * Array of the node divs that hold each result that is being displayed. + * @type {Array} + * @protected + * @suppress {underscore|visibility} + */ + this.rowDivs_ = []; + + /** + * The index of the currently highlighted row + * @type {number} + * @protected + * @suppress {underscore|visibility} + */ + this.hilitedRow_ = -1; + + /** + * The time that the rendering of the menu rows started + * @type {number} + * @protected + * @suppress {underscore|visibility} + */ + this.startRenderingRows_ = -1; + + /** + * Store the current state for the renderer + * @type {boolean} + * @private + */ + this.visible_ = false; + + /** + * Classname for the main element. This must be a single valid class name. + * @type {string} + */ + this.className = goog.getCssName('ac-renderer'); + + /** + * Classname for row divs. This must be a single valid class name. + * @type {string} + */ + this.rowClassName = goog.getCssName('ac-row'); + + // TODO(gboyer): Remove this as soon as we remove references and ensure that + // no groups are pushing javascript using this. + /** + * The old class name for active row. This name is deprecated because its + * name is generic enough that a typical implementation would require a + * descendant selector. + * Active row will have rowClassName & activeClassName & + * legacyActiveClassName. + * @type {string} + * @private + */ + this.legacyActiveClassName_ = goog.getCssName('active'); + + /** + * Class name for active row div. This must be a single valid class name. + * Active row will have rowClassName & activeClassName & + * legacyActiveClassName. + * @type {string} + */ + this.activeClassName = goog.getCssName('ac-active'); + + /** + * Class name for the bold tag highlighting the matched part of the text. + * @type {string} + */ + this.highlightedClassName = goog.getCssName('ac-highlighted'); + + /** + * Custom full renderer + * @type {?({renderRow}|{render})} + * @private + */ + this.customRenderer_ = opt_customRenderer || null; + + /** + * Flag to indicate whether standard highlighting should be applied. + * this is set to true if left unspecified to retain existing + * behaviour for autocomplete clients + * @type {boolean} + * @private + */ + this.useStandardHighlighting_ = + opt_useStandardHighlighting != null ? opt_useStandardHighlighting : true; + + /** + * Flag to indicate whether matches should be done on whole words instead + * of any string. + * @type {boolean} + * @private + */ + this.matchWordBoundary_ = true; + + /** + * Flag to set all tokens as highlighted in the autocomplete row. + * @type {boolean} + * @private + */ + this.highlightAllTokens_ = false; + + /** + * Determines if the autocomplete will always be right aligned + * @type {boolean} + * @private + */ + this.rightAlign_ = !!opt_rightAlign; + + /** + * Whether to align with top of target field + * @type {boolean} + * @private + */ + this.topAlign_ = false; + + /** + * Duration (in msec) of fade animation when menu is shown/hidden. + * Setting to 0 (default) disables animation entirely. + * @type {number} + * @private + */ + this.menuFadeDuration_ = 0; + + /** + * Whether we should limit the dropdown from extending past the bottom of the + * screen and instead show a scrollbar on the dropdown. + * @type {boolean} + * @private + */ + this.showScrollbarsIfTooLarge_ = false; + + /** + * Animation in progress, if any. + * @type {goog.fx.Animation|undefined} + */ + this.animation_; +}; +goog.inherits(goog.ui.ac.Renderer, goog.events.EventTarget); + + +/** + * The anchor element to position the rendered autocompleter against. + * @type {Element} + * @private + */ +goog.ui.ac.Renderer.prototype.anchorElement_; + + +/** + * The anchor element to position the rendered autocompleter against. + * @protected {Element|undefined} + */ +goog.ui.ac.Renderer.prototype.target_; + + +/** + * The element on which to base the width of the autocomplete. + * @protected {Node} + */ +goog.ui.ac.Renderer.prototype.widthProvider_; + + +/** + * The element on which to base the max width of the autocomplete. + * @protected {!Node|undefined} + */ +goog.ui.ac.Renderer.prototype.maxWidthProvider_; + + +/** + * The border width of the autocomplete dropdown, only used in calculating the + * dropdown width. + * @private {number} + */ +goog.ui.ac.Renderer.prototype.borderWidth_ = 0; + + +/** + * A flag used to make sure we highlight only one match in the rendered row. + * @private {boolean} + */ +goog.ui.ac.Renderer.prototype.wasHighlightedAtLeastOnce_; + + +/** + * The delay before mouseover events are registered, in milliseconds + * @type {number} + * @const + */ +goog.ui.ac.Renderer.DELAY_BEFORE_MOUSEOVER = 300; + + +/** + * Gets the renderer's element. + * @return {Element} The main element that controls the rendered autocomplete. + */ +goog.ui.ac.Renderer.prototype.getElement = function() { + return this.element_; +}; + + +/** + * Sets the width provider element. The provider is only used on redraw and as + * such will not automatically update on resize. + * @param {Node} widthProvider The element whose width should be mirrored. + * @param {number=} opt_borderWidth The width of the border of the autocomplete, + * which will be subtracted from the width of the autocomplete dropdown. + * @param {!Node=} maxWidthProvider The element whose width should be used + * as the autocomplete's max width. + */ +goog.ui.ac.Renderer.prototype.setWidthProvider = function( + widthProvider, opt_borderWidth, maxWidthProvider = undefined) { + this.widthProvider_ = widthProvider; + if (opt_borderWidth) { + this.borderWidth_ = opt_borderWidth; + } + if (maxWidthProvider) { + this.maxWidthProvider_ = maxWidthProvider; + } +}; + + +/** + * Set whether to align autocomplete to top of target element + * @param {boolean} align If true, align to top. + */ +goog.ui.ac.Renderer.prototype.setTopAlign = function(align) { + this.topAlign_ = align; +}; + + +/** + * @return {boolean} Whether we should be aligning to the top of + * the target element. + */ +goog.ui.ac.Renderer.prototype.getTopAlign = function() { + return this.topAlign_; +}; + + +/** + * Set whether to align autocomplete to the right of the target element. + * @param {boolean} align If true, align to right. + */ +goog.ui.ac.Renderer.prototype.setRightAlign = function(align) { + this.rightAlign_ = align; +}; + + +/** + * @return {boolean} Whether the autocomplete menu should be right aligned. + */ +goog.ui.ac.Renderer.prototype.getRightAlign = function() { + return this.rightAlign_; +}; + + +/** + * @param {boolean} show Whether we should limit the dropdown from extending + * past the bottom of the screen and instead show a scrollbar on the + * dropdown. + */ +goog.ui.ac.Renderer.prototype.setShowScrollbarsIfTooLarge = function(show) { + this.showScrollbarsIfTooLarge_ = show; +}; + + +/** + * Set whether or not standard highlighting should be used when rendering rows. + * @param {boolean} useStandardHighlighting true if standard highlighting used. + */ +goog.ui.ac.Renderer.prototype.setUseStandardHighlighting = function( + useStandardHighlighting) { + this.useStandardHighlighting_ = useStandardHighlighting; +}; + + +/** + * @param {boolean} matchWordBoundary Determines whether matches should be + * higlighted only when the token matches text at a whole-word boundary. + * True by default. + */ +goog.ui.ac.Renderer.prototype.setMatchWordBoundary = function( + matchWordBoundary) { + this.matchWordBoundary_ = matchWordBoundary; +}; + + +/** + * Set whether or not to highlight all matching tokens rather than just the + * first. + * @param {boolean} highlightAllTokens Whether to highlight all matching tokens + * rather than just the first. + */ +goog.ui.ac.Renderer.prototype.setHighlightAllTokens = function( + highlightAllTokens) { + this.highlightAllTokens_ = highlightAllTokens; +}; + + +/** + * Sets the duration (in msec) of the fade animation when menu is shown/hidden. + * Setting to 0 (default) disables animation entirely. + * @param {number} duration Duration (in msec) of the fade animation (or 0 for + * no animation). + */ +goog.ui.ac.Renderer.prototype.setMenuFadeDuration = function(duration) { + this.menuFadeDuration_ = duration; +}; + + +/** + * Sets the anchor element for the subsequent call to renderRows. + * @param {Element} anchor The anchor element. + */ +goog.ui.ac.Renderer.prototype.setAnchorElement = function(anchor) { + this.anchorElement_ = anchor; +}; + + +/** + * @return {Element} The anchor element. + * @protected + */ +goog.ui.ac.Renderer.prototype.getAnchorElement = function() { + return this.anchorElement_; +}; + + +/** + * Render the autocomplete UI + * + * @param {Array} rows Matching UI rows. + * @param {string} token Token we are currently matching against. + * @param {Element=} opt_target Current HTML node, will position popup beneath + * this node. + */ +goog.ui.ac.Renderer.prototype.renderRows = function(rows, token, opt_target) { + this.token_ = token; + this.rows_ = rows; + this.hilitedRow_ = -1; + this.startRenderingRows_ = goog.now(); + this.target_ = opt_target; + this.rowDivs_ = []; + this.redraw(); +}; + + +/** + * Hide the object. + */ +goog.ui.ac.Renderer.prototype.dismiss = function() { + if (this.visible_) { + this.visible_ = false; + this.toggleAriaMarkup_(false /* isShown */); + + if (this.menuFadeDuration_ > 0) { + goog.dispose(this.animation_); + this.animation_ = + new goog.fx.dom.FadeOutAndHide(this.element_, this.menuFadeDuration_); + this.animation_.play(); + } else { + goog.style.setElementShown(this.element_, false); + } + } +}; + + +/** + * Show the object. + */ +goog.ui.ac.Renderer.prototype.show = function() { + if (!this.visible_) { + this.visible_ = true; + this.toggleAriaMarkup_(true /* isShown */); + + if (this.menuFadeDuration_ > 0) { + goog.dispose(this.animation_); + this.animation_ = + new goog.fx.dom.FadeInAndShow(this.element_, this.menuFadeDuration_); + this.animation_.play(); + } else { + goog.style.setElementShown(this.element_, true); + } + } +}; + + +/** + * Toggle the ARIA markup to add popup semantics when the target is shown and + * to remove them when it is hidden. + * @param {boolean} isShown Whether the menu is being shown. + * @private + */ +goog.ui.ac.Renderer.prototype.toggleAriaMarkup_ = function(isShown) { + if (!this.target_) { + return; + } + + goog.a11y.aria.setState(this.target_, goog.a11y.aria.State.HASPOPUP, isShown); + goog.a11y.aria.setState( + goog.asserts.assert(this.element_), goog.a11y.aria.State.EXPANDED, + isShown); + goog.a11y.aria.setState(this.target_, goog.a11y.aria.State.EXPANDED, isShown); + if (isShown) { + goog.a11y.aria.setState( + this.target_, goog.a11y.aria.State.OWNS, this.element_.id); + } else { + goog.a11y.aria.removeState(this.target_, goog.a11y.aria.State.OWNS); + goog.a11y.aria.setActiveDescendant(this.target_, null); + } +}; + + +/** + * @return {boolean} True if the object is visible. + */ +goog.ui.ac.Renderer.prototype.isVisible = function() { + return this.visible_; +}; + + +/** + * Sets the 'active' class of the nth item. + * @param {number} index Index of the item to highlight. + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.ac.Renderer.prototype.hiliteRow = function(index) { + var row = + index >= 0 && index < this.rows_.length ? this.rows_[index] : undefined; + var rowDiv = index >= 0 && index < this.rowDivs_.length ? + this.rowDivs_[index] : + undefined; + + var evtObj = /** @lends {goog.events.Event.prototype} */ ({ + type: goog.ui.ac.AutoComplete.EventType.ROW_HILITE, + rowNode: rowDiv, + row: row ? row.data : null + }); + if (this.dispatchEvent(evtObj)) { + this.hiliteNone(); + this.hilitedRow_ = index; + if (rowDiv) { + goog.dom.classlist.addAll( + rowDiv, [this.activeClassName, this.legacyActiveClassName_]); + if (this.target_) { + goog.a11y.aria.setActiveDescendant(this.target_, rowDiv); + } + goog.style.scrollIntoContainerView(rowDiv, this.element_); + } + } +}; + + +/** + * Removes the 'active' class from the currently selected row. + */ +goog.ui.ac.Renderer.prototype.hiliteNone = function() { + if (this.hilitedRow_ >= 0) { + goog.dom.classlist.removeAll( + goog.asserts.assert(this.rowDivs_[this.hilitedRow_]), + [this.activeClassName, this.legacyActiveClassName_]); + } +}; + + +/** + * Sets the 'active' class of the item with a given id. + * @param {number} id Id of the row to hilight. If id is -1 then no rows get + * hilited. + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.ac.Renderer.prototype.hiliteId = function(id) { + if (id == -1) { + this.hiliteRow(-1); + } else { + for (var i = 0; i < this.rows_.length; i++) { + if (this.rows_[i].id == id) { + this.hiliteRow(i); + return; + } + } + } +}; + + +/** + * Sets CSS classes on autocomplete conatainer element. + * + * @param {Element} elem The container element. + * @private + */ +goog.ui.ac.Renderer.prototype.setMenuClasses_ = function(elem) { + goog.asserts.assert(elem); + // Legacy clients may set the renderer's className to a space-separated list + // or even have a trailing space. + goog.dom.classlist.addAll(elem, goog.string.trim(this.className).split(' ')); +}; + + +/** + * If the main HTML element hasn't been made yet, creates it and appends it + * to the parent. + * @private + */ +goog.ui.ac.Renderer.prototype.maybeCreateElement_ = function() { + if (!this.element_) { + // Make element and add it to the parent + var el = this.dom_.createDom(goog.dom.TagName.DIV, {style: 'display:none'}); + if (this.showScrollbarsIfTooLarge_) { + // Make sure that the dropdown will get scrollbars if it isn't large + // enough to show all rows. + el.style.overflowY = 'auto'; + } + this.element_ = el; + this.setMenuClasses_(el); + goog.a11y.aria.setRole(el, goog.a11y.aria.Role.LISTBOX); + + el.id = goog.ui.IdGenerator.getInstance().getNextUniqueId(); + + this.dom_.appendChild(this.parent_, el); + + // Add this object as an event handler + goog.events.listen( + el, goog.events.EventType.CLICK, this.handleClick_, false, this); + goog.events.listen( + el, goog.events.EventType.MOUSEDOWN, this.handleMouseDown_, false, + this); + goog.events.listen( + el, goog.events.EventType.MOUSEOVER, this.handleMouseOver_, false, + this); + } +}; + + +/** + * Redraw (or draw if this is the first call) the rendered auto-complete drop + * down. + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.ac.Renderer.prototype.redraw = function() { + // Create the element if it doesn't yet exist + this.maybeCreateElement_(); + + // For top aligned with target (= bottom aligned element), + // we need to hide and then add elements while hidden to prevent + // visible repositioning + if (this.topAlign_) { + this.element_.style.visibility = 'hidden'; + } + + if (this.widthProvider_) { + var width = this.widthProvider_.clientWidth - this.borderWidth_ + 'px'; + this.element_.style.minWidth = width; + } + if (this.maxWidthProvider_) { + const maxWidth = + this.maxWidthProvider_.clientWidth - this.borderWidth_ + 'px'; + this.element_.style.maxWidth = maxWidth; + } + + // Remove the current child nodes + this.rowDivs_.length = 0; + this.dom_.removeChildren(this.element_); + + // Generate the new rows (use forEach so we can change rows_ from an + // array to a different datastructure if required) + if (this.customRenderer_ && this.customRenderer_.render) { + this.customRenderer_.render(this, this.element_, this.rows_, this.token_); + } else { + var curRow = null; + goog.array.forEach(this.rows_, function(row) { + row = this.renderRowHtml(row, this.token_); + if (this.topAlign_) { + // Aligned with top of target = best match at bottom + this.element_.insertBefore(row, curRow); + } else { + this.dom_.appendChild(this.element_, row); + } + curRow = row; + }, this); + } + + // Don't show empty result sets + if (this.rows_.length == 0) { + this.dismiss(); + return; + } else { + this.show(); + } + + this.reposition(); + + // Make the autocompleter unselectable, so that it + // doesn't steal focus from the input field when clicked. + goog.style.setUnselectable(this.element_, true); +}; + + +/** + * @return {goog.positioning.Corner} The anchor corner to position the popup at. + * @protected + */ +goog.ui.ac.Renderer.prototype.getAnchorCorner = function() { + var anchorCorner = this.rightAlign_ ? goog.positioning.Corner.BOTTOM_RIGHT : + goog.positioning.Corner.BOTTOM_LEFT; + if (this.topAlign_) { + anchorCorner = goog.positioning.flipCornerVertical(anchorCorner); + } + return anchorCorner; +}; + + +/** + * Repositions the auto complete popup relative to the location node, if it + * exists and the auto position has been set. + */ +goog.ui.ac.Renderer.prototype.reposition = function() { + if (this.target_ && this.reposition_) { + var anchorElement = this.anchorElement_ || this.target_; + var anchorCorner = this.getAnchorCorner(); + + var overflowMode = goog.positioning.Overflow.ADJUST_X_EXCEPT_OFFSCREEN; + if (this.showScrollbarsIfTooLarge_) { + // positionAtAnchor will set the height of this.element_ when it runs + // (because of RESIZE_HEIGHT), and it will never increase it relative to + // its current value when it runs again. But if the user scrolls their + // page, then we might actually want a bigger height when the dropdown is + // displayed next time. So we clear the height before calling + // positionAtAnchor, so it is free to set the height as large as it + // chooses. + this.element_.style.height = ''; + overflowMode |= goog.positioning.Overflow.RESIZE_HEIGHT; + } + + goog.positioning.positionAtAnchor( + anchorElement, anchorCorner, this.element_, + goog.positioning.flipCornerVertical(anchorCorner), null, null, + overflowMode); + + if (this.topAlign_) { + // This flickers, but is better than the alternative of positioning + // in the wrong place and then moving. + this.element_.style.visibility = 'visible'; + } + } +}; + + +/** + * Sets whether the renderer should try to determine where to position the + * drop down. + * @param {boolean} auto Whether to autoposition the drop down. + */ +goog.ui.ac.Renderer.prototype.setAutoPosition = function(auto) { + this.reposition_ = auto; +}; + + +/** + * @return {boolean} Whether the drop down will be autopositioned. + * @protected + */ +goog.ui.ac.Renderer.prototype.getAutoPosition = function() { + return this.reposition_; +}; + + +/** + * @return {Element} The target element. + * @protected + */ +goog.ui.ac.Renderer.prototype.getTarget = function() { + return this.target_ || null; +}; + + +/** + * Disposes of the renderer and its associated HTML. + * @override + * @protected + */ +goog.ui.ac.Renderer.prototype.disposeInternal = function() { + if (this.element_) { + goog.events.unlisten( + this.element_, goog.events.EventType.CLICK, this.handleClick_, false, + this); + goog.events.unlisten( + this.element_, goog.events.EventType.MOUSEDOWN, this.handleMouseDown_, + false, this); + goog.events.unlisten( + this.element_, goog.events.EventType.MOUSEOVER, this.handleMouseOver_, + false, this); + this.dom_.removeNode(this.element_); + this.element_ = null; + this.visible_ = false; + } + + goog.dispose(this.animation_); + this.parent_ = null; + + goog.ui.ac.Renderer.base(this, 'disposeInternal'); +}; + + +/** + * Generic function that takes a row and renders a DOM structure for that row. + * + * Normally this will only be matching a maximum of 20 or so items. Even with + * 40 rows, DOM this building is fine. + * @param {Object} row Object representing row. + * @param {string} token Token to highlight. + * @param {Node} node The node to render into. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.ac.Renderer.prototype.renderRowContents_ = function(row, token, node) { + goog.dom.setTextContent(node, row.data.toString()); +}; + + +/** + * Goes through a node and all of its child nodes, replacing HTML text that + * matches a token with token. + * The replacement will happen on the first match or all matches depending on + * this.highlightAllTokens_ value. + * + * @param {Node} node Node to match. + * @param {string|Array} tokenOrArray Token to match or array of tokens + * to match. By default, only the first match will be highlighted. If + * highlightAllTokens is set, then all tokens appearing at the start of a + * word, in whatever order and however many times, will be highlighted. + * @private + */ +goog.ui.ac.Renderer.prototype.startHiliteMatchingText_ = function( + node, tokenOrArray) { + this.wasHighlightedAtLeastOnce_ = false; + this.hiliteMatchingText_(node, tokenOrArray); +}; + + +/** + * @param {Node} node Node to match. + * @param {string|Array} tokenOrArray Token to match or array of tokens + * to match. + * @private + */ +goog.ui.ac.Renderer.prototype.hiliteMatchingText_ = function( + node, tokenOrArray) { + if (!this.highlightAllTokens_ && this.wasHighlightedAtLeastOnce_) { + return; + } + + if (node.nodeType == goog.dom.NodeType.TEXT) { + var rest = null; + if (goog.isArray(tokenOrArray) && tokenOrArray.length > 1 && + !this.highlightAllTokens_) { + rest = goog.array.slice(tokenOrArray, 1); + } + + var token = this.getTokenRegExp_(tokenOrArray); + if (token.length == 0) return; + + var text = node.nodeValue; + + // Create a regular expression to match a token at the beginning of a line + // or preceded by non-alpha-numeric characters. Note: token could have | + // operators in it, so we need to parenthesise it before adding \b to it. + // or preceded by non-alpha-numeric characters + // + // NOTE(user): When using word matches, this used to have + // a (^|\\W+) clause where it now has \\b but it caused various + // browsers to hang on really long strings. The (^|\\W+) matcher was also + // unnecessary, because \b already checks that the character before the + // is a non-word character, and ^ matches the start of the line or following + // a line terminator character, which is also \W. The regexp also used to + // have a capturing match before the \\b, which would capture the + // non-highlighted content, but that caused the regexp matching to run much + // slower than the current version. + var re = this.matchWordBoundary_ ? + new RegExp('\\b(?:' + token + ')', 'gi') : + new RegExp(token, 'gi'); + var textNodes = []; + var lastIndex = 0; + + // Find all matches + // Note: text.split(re) has inconsistencies between IE and FF, so + // manually recreated the logic + var match = re.exec(text); + var numMatches = 0; + while (match) { + numMatches++; + textNodes.push(text.substring(lastIndex, match.index)); + textNodes.push(text.substring(match.index, re.lastIndex)); + lastIndex = re.lastIndex; + match = re.exec(text); + } + textNodes.push(text.substring(lastIndex)); + + // Replace the tokens with bolded text. Each pair of textNodes + // (starting at index idx) includes a node of text before the bolded + // token, and a node (at idx + 1) consisting of what should be + // enclosed in bold tags. + if (textNodes.length > 1) { + var maxNumToBold = !this.highlightAllTokens_ ? 1 : numMatches; + for (var i = 0; i < maxNumToBold; i++) { + var idx = 2 * i; + + node.nodeValue = textNodes[idx]; + var boldTag = this.dom_.createElement(goog.dom.TagName.B); + boldTag.className = this.highlightedClassName; + this.dom_.appendChild( + boldTag, this.dom_.createTextNode(textNodes[idx + 1])); + boldTag = node.parentNode.insertBefore(boldTag, node.nextSibling); + node.parentNode.insertBefore( + this.dom_.createTextNode(''), boldTag.nextSibling); + node = boldTag.nextSibling; + } + + // Append the remaining text nodes to the end. + var remainingTextNodes = goog.array.slice(textNodes, maxNumToBold * 2); + node.nodeValue = remainingTextNodes.join(''); + + this.wasHighlightedAtLeastOnce_ = true; + } else if (rest) { + this.hiliteMatchingText_(node, rest); + } + } else { + var child = node.firstChild; + while (child) { + var nextChild = child.nextSibling; + this.hiliteMatchingText_(child, tokenOrArray); + child = nextChild; + } + } +}; + + +/** + * Transforms a token into a string ready to be put into the regular expression + * in hiliteMatchingText_. + * @param {string|Array} tokenOrArray The token or array to get the + * regex string from. + * @return {string} The regex-ready token. + * @private + */ +goog.ui.ac.Renderer.prototype.getTokenRegExp_ = function(tokenOrArray) { + var token = ''; + + if (!tokenOrArray) { + return token; + } + + if (goog.isArray(tokenOrArray)) { + // Remove invalid tokens from the array, which may leave us with nothing. + tokenOrArray = goog.array.filter(tokenOrArray, function(str) { + return !goog.string.isEmptyOrWhitespace(goog.string.makeSafe(str)); + }); + } + + // If highlighting all tokens, join them with '|' so the regular expression + // will match on any of them. + if (this.highlightAllTokens_) { + if (goog.isArray(tokenOrArray)) { + var tokenArray = goog.array.map(tokenOrArray, goog.string.regExpEscape); + token = tokenArray.join('|'); + } else { + // Remove excess whitespace from the string so bars will separate valid + // tokens in the regular expression. + token = goog.string.collapseWhitespace(tokenOrArray); + + token = goog.string.regExpEscape(token); + token = token.replace(/ /g, '|'); + } + } else { + // Not highlighting all matching tokens. If tokenOrArray is a string, use + // that as the token. If it is an array, use the first element in the + // array. + // TODO(user): why is this this way?. We should match against all + // tokens in the array, but only accept the first match. + if (goog.isArray(tokenOrArray)) { + token = tokenOrArray.length > 0 ? + goog.string.regExpEscape(tokenOrArray[0]) : + ''; + } else { + // For the single-match string token, we refuse to match anything if + // the string begins with a non-word character, as matches by definition + // can only occur at the start of a word. (This also handles the + // goog.string.isEmptyOrWhitespace(goog.string.makeSafe(tokenOrArray)) + // case.) + if (!/^\W/.test(tokenOrArray)) { + token = goog.string.regExpEscape(tokenOrArray); + } + } + } + + return token; +}; + + +/** + * Render a row by creating a div and then calling row rendering callback or + * default row handler + * + * @param {Object} row Object representing row. + * @param {string} token Token to highlight. + * @return {!Element} An element with the rendered HTML. + */ +goog.ui.ac.Renderer.prototype.renderRowHtml = function(row, token) { + // Create and return the element. + var elem = this.dom_.createDom(goog.dom.TagName.DIV, { + className: this.rowClassName, + id: goog.ui.IdGenerator.getInstance().getNextUniqueId() + }); + goog.a11y.aria.setRole(elem, goog.a11y.aria.Role.OPTION); + if (this.customRenderer_ && this.customRenderer_.renderRow) { + this.customRenderer_.renderRow(row, token, elem); + } else { + this.renderRowContents_(row, token, elem); + } + + if (token && this.useStandardHighlighting_) { + this.startHiliteMatchingText_(elem, token); + } + + goog.dom.classlist.add(elem, this.rowClassName); + this.rowDivs_.push(elem); + return elem; +}; + + +/** + * Given an event target looks up through the parents till it finds a div. Once + * found it will then look to see if that is one of the childnodes, if it is + * then the index is returned, otherwise -1 is returned. + * @param {Element} et HtmlElement. + * @return {number} Index corresponding to event target. + * @private + */ +goog.ui.ac.Renderer.prototype.getRowFromEventTarget_ = function(et) { + while (et && et != this.element_ && + !goog.dom.classlist.contains(et, this.rowClassName)) { + et = /** @type {Element} */ (et.parentNode); + } + return et ? goog.array.indexOf(this.rowDivs_, et) : -1; +}; + + +/** + * Handle the click events. These are redirected to the AutoComplete object + * which then makes a callback to select the correct row. + * @param {goog.events.Event} e Browser event object. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.ac.Renderer.prototype.handleClick_ = function(e) { + var index = this.getRowFromEventTarget_(/** @type {Element} */ (e.target)); + if (index >= 0) { + this.dispatchEvent(/** @lends {goog.events.Event.prototype} */ ({ + type: goog.ui.ac.AutoComplete.EventType.SELECT, + row: this.rows_[index].id + })); + } + e.stopPropagation(); +}; + + +/** + * Handle the mousedown event and prevent the AC from losing focus. + * @param {goog.events.Event} e Browser event object. + * @private + */ +goog.ui.ac.Renderer.prototype.handleMouseDown_ = function(e) { + e.stopPropagation(); + e.preventDefault(); +}; + + +/** + * Handle the mousing events. These are redirected to the AutoComplete object + * which then makes a callback to set the correctly highlighted row. This is + * because the AutoComplete can move the focus as well, and there is no sense + * duplicating the code + * @param {goog.events.Event} e Browser event object. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.ac.Renderer.prototype.handleMouseOver_ = function(e) { + var index = this.getRowFromEventTarget_(/** @type {Element} */ (e.target)); + if (index >= 0) { + if ((goog.now() - this.startRenderingRows_) < + goog.ui.ac.Renderer.DELAY_BEFORE_MOUSEOVER) { + return; + } + + this.dispatchEvent({ + type: goog.ui.ac.AutoComplete.EventType.HILITE, + row: this.rows_[index].id + }); + } +}; + + + +/** + * Class allowing different implementations to custom render the autocomplete. + * Extending classes should override the render function. + * @constructor + */ +goog.ui.ac.Renderer.CustomRenderer = function() {}; + + +/** + * Renders the autocomplete box. May be set to null. + * + * Because of the type, this function cannot be documented with param JSDoc. + * + * The function expects the following parameters: + * + * renderer, goog.ui.ac.Renderer: The autocomplete renderer. + * element, Element: The main element that controls the rendered autocomplete. + * rows, Array: The current set of rows being displayed. + * token, string: The current token that has been entered. * + * + * @type {function(goog.ui.ac.Renderer, Element, Array, string)| + * null|undefined} + */ +goog.ui.ac.Renderer.CustomRenderer.prototype.render = function( + renderer, element, rows, token) {}; + + +/** + * Generic function that takes a row and renders a DOM structure for that row. + * @param {Object} row Object representing row. + * @param {string} token Token to highlight. + * @param {Node} node The node to render into. + */ +goog.ui.ac.Renderer.CustomRenderer.prototype.renderRow = function( + row, token, node) {}; diff --git a/closure-library/closure/goog/ui/ac/renderoptions.js b/closure-library/closure/goog/ui/ac/renderoptions.js new file mode 100644 index 0000000000..13dcb9cd7f --- /dev/null +++ b/closure-library/closure/goog/ui/ac/renderoptions.js @@ -0,0 +1,79 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Options for rendering matches. + * + */ + +goog.provide('goog.ui.ac.RenderOptions'); + + + +/** + * A simple class that contains options for rendering a set of autocomplete + * matches. Used as an optional argument in the callback from the matcher. + * @constructor + */ +goog.ui.ac.RenderOptions = function() {}; + + +/** + * Whether the current highlighting is to be preserved when displaying the new + * set of matches. + * @type {boolean} + * @private + */ +goog.ui.ac.RenderOptions.prototype.preserveHilited_ = false; + + +/** + * Whether the first match is to be highlighted. When undefined the autoHilite + * flag of the autocomplete is used. + * @type {boolean|undefined} + * @private + */ +goog.ui.ac.RenderOptions.prototype.autoHilite_; + + +/** + * @param {boolean} flag The new value for the preserveHilited_ flag. + */ +goog.ui.ac.RenderOptions.prototype.setPreserveHilited = function(flag) { + this.preserveHilited_ = flag; +}; + + +/** + * @return {boolean} The value of the preserveHilited_ flag. + */ +goog.ui.ac.RenderOptions.prototype.getPreserveHilited = function() { + return this.preserveHilited_; +}; + + +/** + * @param {boolean} flag The new value for the autoHilite_ flag. + */ +goog.ui.ac.RenderOptions.prototype.setAutoHilite = function(flag) { + this.autoHilite_ = flag; +}; + + +/** + * @return {boolean|undefined} The value of the autoHilite_ flag. + */ +goog.ui.ac.RenderOptions.prototype.getAutoHilite = function() { + return this.autoHilite_; +}; diff --git a/closure-library/closure/goog/ui/ac/richinputhandler.js b/closure-library/closure/goog/ui/ac/richinputhandler.js new file mode 100644 index 0000000000..32fde42b4c --- /dev/null +++ b/closure-library/closure/goog/ui/ac/richinputhandler.js @@ -0,0 +1,58 @@ +// Copyright 2007 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 Class for managing the interactions between a rich autocomplete + * object and a text-input or textarea. + * + */ + +goog.provide('goog.ui.ac.RichInputHandler'); + +goog.require('goog.ui.ac.InputHandler'); + + + +/** + * Class for managing the interaction between an autocomplete object and a + * text-input or textarea. + * @param {?string=} opt_separators Seperators to split multiple entries. + * @param {?string=} opt_literals Characters used to delimit text literals. + * @param {?boolean=} opt_multi Whether to allow multiple entries + * (Default: true). + * @param {?number=} opt_throttleTime Number of milliseconds to throttle + * keyevents with (Default: 150). + * @constructor + * @extends {goog.ui.ac.InputHandler} + */ +goog.ui.ac.RichInputHandler = function( + opt_separators, opt_literals, opt_multi, opt_throttleTime) { + goog.ui.ac.InputHandler.call( + this, opt_separators, opt_literals, opt_multi, opt_throttleTime); +}; +goog.inherits(goog.ui.ac.RichInputHandler, goog.ui.ac.InputHandler); + + +/** + * Selects the given rich row. The row's select(target) method is called. + * @param {Object} row The row to select. + * @return {boolean} Whether to suppress the update event. + * @override + */ +goog.ui.ac.RichInputHandler.prototype.selectRow = function(row) { + var suppressUpdate = + goog.ui.ac.RichInputHandler.superClass_.selectRow.call(this, row); + row.select(this.ac_.getTarget()); + return suppressUpdate; +}; diff --git a/closure-library/closure/goog/ui/ac/richremote.js b/closure-library/closure/goog/ui/ac/richremote.js new file mode 100644 index 0000000000..209685ca04 --- /dev/null +++ b/closure-library/closure/goog/ui/ac/richremote.js @@ -0,0 +1,116 @@ +// Copyright 2007 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 Factory class to create a rich autocomplete that will match + * from an array of data provided via ajax. The server returns a complex data + * structure that is used with client-side javascript functions to render the + * results. + * + * The server sends a list of the form: + * [["type1", {...}, {...}, ...], ["type2", {...}, {...}, ...], ...] + * The first element of each sublist is a string designating the type of the + * hashes in the sublist, each of which represents one match. The type string + * must be the name of a function(item) which converts the hash into a rich + * row that contains both a render(node, token) and a select(target) method. + * The render method is called by the renderer when rendering the rich row, + * and the select method is called by the RichInputHandler when the rich row is + * selected. + * + * @see ../../demos/autocompleterichremote.html + */ + +goog.provide('goog.ui.ac.RichRemote'); + +goog.require('goog.ui.ac.AutoComplete'); +goog.require('goog.ui.ac.Remote'); +goog.require('goog.ui.ac.Renderer'); +goog.require('goog.ui.ac.RichInputHandler'); +goog.require('goog.ui.ac.RichRemoteArrayMatcher'); + + + +/** + * Factory class to create a rich autocomplete widget that autocompletes an + * inputbox or textarea from data provided via ajax. The server returns a + * complex data structure that is used with client-side javascript functions to + * render the results. + * + * @param {string} url The Uri which generates the auto complete matches. + * @param {Element} input Input element or text area. + * @param {boolean=} opt_multi Whether to allow multiple entries; defaults + * to false. + * @param {boolean=} opt_useSimilar Whether to use similar matches; e.g. + * "gost" => "ghost". + * @constructor + * @extends {goog.ui.ac.Remote} + */ +goog.ui.ac.RichRemote = function(url, input, opt_multi, opt_useSimilar) { + // Create a custom renderer that renders rich rows. The renderer calls + // row.render(node, token) for each row. + var customRenderer = {}; + customRenderer.renderRow = function(row, token, node) { + return row.data.render(node, token); + }; + + /** + * A standard renderer that uses a custom row renderer to display the + * rich rows generated by this autocomplete widget. + * @type {goog.ui.ac.Renderer} + * @private + */ + var renderer = new goog.ui.ac.Renderer(null, customRenderer); + + /** + * A remote matcher that parses rich results returned by the server. + * @type {goog.ui.ac.RichRemoteArrayMatcher} + * @private + */ + var matcher = new goog.ui.ac.RichRemoteArrayMatcher(url, !opt_useSimilar); + + /** + * An input handler that calls select on a row when it is selected. + * @type {goog.ui.ac.RichInputHandler} + * @private + */ + var inputhandler = + new goog.ui.ac.RichInputHandler(null, null, !!opt_multi, 300); + + // Create the widget and connect it to the input handler. + goog.ui.ac.AutoComplete.call(this, matcher, renderer, inputhandler); + inputhandler.attachAutoComplete(this); + inputhandler.attachInputs(input); +}; +goog.inherits(goog.ui.ac.RichRemote, goog.ui.ac.Remote); + + +/** + * Set the filter that is called before the array matches are returned. + * @param {Function} rowFilter A function(rows) that returns an array of rows as + * a subset of the rows input array. + */ +goog.ui.ac.RichRemote.prototype.setRowFilter = function(rowFilter) { + this.matcher_.setRowFilter(rowFilter); +}; + + +/** + * Sets the function building the rows. + * @param {goog.ui.ac.RichRemoteArrayMatcher.RowBuilder} rowBuilder + * A function(type, response) converting the type and the server response to + * an object with two methods: render(node, token) and select(target). + */ +goog.ui.ac.RichRemote.prototype.setRowBuilder = function(rowBuilder) { + this.matcher_.setRowBuilder(rowBuilder); +}; diff --git a/closure-library/closure/goog/ui/ac/richremotearraymatcher.js b/closure-library/closure/goog/ui/ac/richremotearraymatcher.js new file mode 100644 index 0000000000..c8afc2b10c --- /dev/null +++ b/closure-library/closure/goog/ui/ac/richremotearraymatcher.js @@ -0,0 +1,153 @@ +// Copyright 2007 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 Class that retrieves rich autocomplete matches, represented as + * a structured list of lists, via an ajax call. The first element of each + * sublist is the name of a client-side javascript function that converts the + * remaining sublist elements into rich rows. + * + */ + +goog.provide('goog.ui.ac.RichRemoteArrayMatcher'); + +goog.require('goog.dom'); +goog.require('goog.ui.ac.RemoteArrayMatcher'); + + + +/** + * An array matcher that requests rich matches via ajax and converts them into + * rich rows. + * + * @param {string} url The Uri which generates the auto complete matches. The + * search term is passed to the server as the 'token' query param. + * @param {boolean=} opt_noSimilar If true, request that the server does not do + * similarity matches for the input token against the dictionary. + * The value is sent to the server as the 'use_similar' query param which is + * either "1" (opt_noSimilar==false) or "0" (opt_noSimilar==true). + * @constructor + * @extends {goog.ui.ac.RemoteArrayMatcher} + */ +goog.ui.ac.RichRemoteArrayMatcher = function(url, opt_noSimilar) { + goog.ui.ac.RemoteArrayMatcher.call(this, url, opt_noSimilar); + + /** + * A function(rows) that is called before the array matches are returned. + * It runs client-side and filters the results given by the server before + * being rendered by the client. + * @type {Function} + * @private + */ + this.rowFilter_ = null; + + /** + * A function(type, response) converting the type and the server response to + * an object with two methods: render(node, token) and select(target). + * @private {goog.ui.ac.RichRemoteArrayMatcher.RowBuilder} + */ + this.rowBuilder_ = function(type, response) { + return /** @type {!Object} */ (response); + }; +}; +goog.inherits(goog.ui.ac.RichRemoteArrayMatcher, goog.ui.ac.RemoteArrayMatcher); + + +/** + * Set the filter that is called before the array matches are returned. + * @param {Function} rowFilter A function(rows) that returns an array of rows as + * a subset of the rows input array. + */ +goog.ui.ac.RichRemoteArrayMatcher.prototype.setRowFilter = function(rowFilter) { + this.rowFilter_ = rowFilter; +}; + + +/** + * @typedef {function(string, *): { + * render: (function(!Element, string)|undefined), + * select: (function(!Element)|undefined) + * }} + */ +goog.ui.ac.RichRemoteArrayMatcher.RowBuilder; + + +/** + * Sets the function building the rows. + * @param {goog.ui.ac.RichRemoteArrayMatcher.RowBuilder} rowBuilder + * A function(type, response) converting the type and the server response to + * an object with two methods: render(node, token) and select(target). + */ +goog.ui.ac.RichRemoteArrayMatcher.prototype.setRowBuilder = function( + rowBuilder) { + this.rowBuilder_ = rowBuilder; +}; + + +/** + * Retrieve a set of matching rows from the server via ajax and convert them + * into rich rows. + * @param {string} token The text that should be matched; passed to the server + * as the 'token' query param. + * @param {number} maxMatches The maximum number of matches requested from the + * server; passed as the 'max_matches' query param. The server is + * responsible for limiting the number of matches that are returned. + * @param {Function} matchHandler Callback to execute on the result after + * matching. + * @override + */ +goog.ui.ac.RichRemoteArrayMatcher.prototype.requestMatchingRows = function( + token, maxMatches, matchHandler) { + // The RichRemoteArrayMatcher must map over the results and filter them + // before calling the request matchHandler. This is done by passing + // myMatchHandler to RemoteArrayMatcher.requestMatchingRows which maps, + // filters, and then calls matchHandler. + var myMatchHandler = goog.bind(function(token, matches) { + + try { + var rows = []; + for (var i = 0; i < matches.length; i++) { + for (var j = 1; j < matches[i].length; j++) { + var richRow = this.rowBuilder_(matches[i][0], matches[i][j]); + rows.push(richRow); + + // If no render function was provided, set the node's textContent. + if (typeof richRow.render == 'undefined') { + richRow.render = function(node, token) { + goog.dom.setTextContent(node, richRow.toString()); + }; + } + + // If no select function was provided, set the text of the input. + if (typeof richRow.select == 'undefined') { + richRow.select = function(target) { + target.value = richRow.toString(); + }; + } + } + } + if (this.rowFilter_) { + rows = this.rowFilter_(rows); + } + matchHandler(token, rows); + } catch (exception) { + // TODO(user): Is this what we want? + matchHandler(token, []); + } + }, this); + + // Call the super's requestMatchingRows with myMatchHandler + goog.ui.ac.RichRemoteArrayMatcher.superClass_.requestMatchingRows.call( + this, token, maxMatches, myMatchHandler); +}; diff --git a/closure-library/closure/goog/ui/activitymonitor.js b/closure-library/closure/goog/ui/activitymonitor.js new file mode 100644 index 0000000000..35f8343f35 --- /dev/null +++ b/closure-library/closure/goog/ui/activitymonitor.js @@ -0,0 +1,345 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Activity Monitor. + * + * Fires throttled events when a user interacts with the specified document. + * This class also exposes the amount of time since the last user event. + * + * If you would prefer to get BECOME_ACTIVE and BECOME_IDLE events when the + * user changes states, then you should use the IdleTimer class instead. + * + */ + +goog.provide('goog.ui.ActivityMonitor'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.events.EventHandler'); +goog.require('goog.events.EventTarget'); +goog.require('goog.events.EventType'); + + + +/** + * Once initialized with a document, the activity monitor can be queried for + * the current idle time. + * + * @param {goog.dom.DomHelper|Array=} opt_domHelper + * DomHelper which contains the document(s) to listen to. If null, the + * default document is usedinstead. + * @param {boolean=} opt_useBubble Whether to use the bubble phase to listen for + * events. By default listens on the capture phase so that it won't miss + * events that get stopPropagation/cancelBubble'd. However, this can cause + * problems in IE8 if the page loads multiple scripts that include the + * closure event handling code. + * + * @constructor + * @extends {goog.events.EventTarget} + */ +goog.ui.ActivityMonitor = function(opt_domHelper, opt_useBubble) { + goog.events.EventTarget.call(this); + + /** + * Array of documents that are being listened to. + * @type {Array} + * @private + */ + this.documents_ = []; + + /** + * Whether to use the bubble phase to listen for events. + * @type {boolean} + * @private + */ + this.useBubble_ = !!opt_useBubble; + + /** + * The event handler. + * @type {goog.events.EventHandler} + * @private + */ + this.eventHandler_ = new goog.events.EventHandler(this); + + /** + * Whether the current window is an iframe. + * TODO(user): Move to goog.dom. + * @type {boolean} + * @private + */ + this.isIframe_ = window.parent != window; + + if (!opt_domHelper) { + this.addDocument(goog.dom.getDomHelper().getDocument()); + } else if (goog.isArray(opt_domHelper)) { + for (var i = 0; i < opt_domHelper.length; i++) { + this.addDocument(opt_domHelper[i].getDocument()); + } + } else { + this.addDocument(opt_domHelper.getDocument()); + } + + /** + * The time (in milliseconds) of the last user event. + * @type {number} + * @private + */ + this.lastEventTime_ = goog.now(); + +}; +goog.inherits(goog.ui.ActivityMonitor, goog.events.EventTarget); +goog.tagUnsealableClass(goog.ui.ActivityMonitor); + + +/** + * The last event type that was detected. + * @type {string} + * @private + */ +goog.ui.ActivityMonitor.prototype.lastEventType_ = ''; + + +/** + * The mouse x-position after the last user event. + * @type {number} + * @private + */ +goog.ui.ActivityMonitor.prototype.lastMouseX_; + + +/** + * The mouse y-position after the last user event. + * @type {number} + * @private + */ +goog.ui.ActivityMonitor.prototype.lastMouseY_; + + +/** + * The earliest time that another throttled ACTIVITY event will be dispatched + * @type {number} + * @private + */ +goog.ui.ActivityMonitor.prototype.minEventTime_ = 0; + + +/** + * Minimum amount of time in ms between throttled ACTIVITY events + * @type {number} + */ +goog.ui.ActivityMonitor.MIN_EVENT_SPACING = 3 * 1000; + + +/** + * If a user executes one of these events, s/he is considered not idle. + * @type {Array} + * @private + */ +goog.ui.ActivityMonitor.userEventTypesBody_ = [ + goog.events.EventType.CLICK, goog.events.EventType.DBLCLICK, + goog.events.EventType.MOUSEDOWN, goog.events.EventType.MOUSEMOVE, + goog.events.EventType.MOUSEUP +]; + + +/** + * If a user executes one of these events, s/he is considered not idle. + * Note: monitoring touch events within iframe cause problems in iOS. + * @type {Array} + * @private + */ +goog.ui.ActivityMonitor.userTouchEventTypesBody_ = [ + goog.events.EventType.TOUCHEND, goog.events.EventType.TOUCHMOVE, + goog.events.EventType.TOUCHSTART +]; + + +/** + * If a user executes one of these events, s/he is considered not idle. + * @type {Array} + * @private + */ +goog.ui.ActivityMonitor.userEventTypesDocuments_ = + [goog.events.EventType.KEYDOWN, goog.events.EventType.KEYUP]; + + +/** + * Event constants for the activity monitor. + * @enum {string} + */ +goog.ui.ActivityMonitor.Event = { + /** Event fired when the user does something interactive */ + ACTIVITY: 'activity' +}; + + +/** @override */ +goog.ui.ActivityMonitor.prototype.disposeInternal = function() { + goog.ui.ActivityMonitor.superClass_.disposeInternal.call(this); + this.eventHandler_.dispose(); + this.eventHandler_ = null; + delete this.documents_; +}; + + +/** + * Adds a document to those being monitored by this class. + * + * @param {Document} doc Document to monitor. + */ +goog.ui.ActivityMonitor.prototype.addDocument = function(doc) { + if (goog.array.contains(this.documents_, doc)) { + return; + } + this.documents_.push(doc); + var useCapture = !this.useBubble_; + + var eventsToListenTo = goog.array.concat( + goog.ui.ActivityMonitor.userEventTypesDocuments_, + goog.ui.ActivityMonitor.userEventTypesBody_); + + if (!this.isIframe_) { + // Monitoring touch events in iframe causes problems interacting with text + // fields in iOS (input text, textarea, contenteditable, select/copy/paste), + // so just ignore these events. This shouldn't matter much given that a + // touchstart event followed by touchend event produces a click event, + // which is being monitored correctly. + goog.array.extend( + eventsToListenTo, goog.ui.ActivityMonitor.userTouchEventTypesBody_); + } + + this.eventHandler_.listen( + doc, eventsToListenTo, this.handleEvent_, useCapture); +}; + + +/** + * Removes a document from those being monitored by this class. + * + * @param {Document} doc Document to monitor. + */ +goog.ui.ActivityMonitor.prototype.removeDocument = function(doc) { + if (this.isDisposed()) { + return; + } + goog.array.remove(this.documents_, doc); + var useCapture = !this.useBubble_; + + var eventsToUnlistenTo = goog.array.concat( + goog.ui.ActivityMonitor.userEventTypesDocuments_, + goog.ui.ActivityMonitor.userEventTypesBody_); + + if (!this.isIframe_) { + // See note above about monitoring touch events in iframe. + goog.array.extend( + eventsToUnlistenTo, goog.ui.ActivityMonitor.userTouchEventTypesBody_); + } + + this.eventHandler_.unlisten( + doc, eventsToUnlistenTo, this.handleEvent_, useCapture); +}; + + +/** + * Updates the last event time when a user action occurs. + * @param {goog.events.BrowserEvent} e Event object. + * @private + */ +goog.ui.ActivityMonitor.prototype.handleEvent_ = function(e) { + var update = false; + switch (e.type) { + case goog.events.EventType.MOUSEMOVE: + // In FF 1.5, we get spurious mouseover and mouseout events when the UI + // redraws. We only want to update the idle time if the mouse has moved. + if (typeof this.lastMouseX_ == 'number' && + this.lastMouseX_ != e.clientX || + typeof this.lastMouseY_ == 'number' && + this.lastMouseY_ != e.clientY) { + update = true; + } + this.lastMouseX_ = e.clientX; + this.lastMouseY_ = e.clientY; + break; + default: + update = true; + } + + if (update) { + var type = goog.asserts.assertString(e.type); + this.updateIdleTime(goog.now(), type); + } +}; + + +/** + * Updates the last event time to be the present time, useful for non-DOM + * events that should update idle time. + */ +goog.ui.ActivityMonitor.prototype.resetTimer = function() { + this.updateIdleTime(goog.now(), 'manual'); +}; + + +/** + * Updates the idle time and fires an event if time has elapsed since + * the last update. + * @param {number} eventTime Time (in MS) of the event that cleared the idle + * timer. + * @param {string} eventType Type of the event, used only for debugging. + * @protected + */ +goog.ui.ActivityMonitor.prototype.updateIdleTime = function( + eventTime, eventType) { + // update internal state noting whether the user was idle + this.lastEventTime_ = eventTime; + this.lastEventType_ = eventType; + + // dispatch event + if (eventTime > this.minEventTime_) { + this.dispatchEvent(goog.ui.ActivityMonitor.Event.ACTIVITY); + this.minEventTime_ = eventTime + goog.ui.ActivityMonitor.MIN_EVENT_SPACING; + } +}; + + +/** + * Returns the amount of time the user has been idle. + * @param {number=} opt_now The current time can optionally be passed in for the + * computation to avoid an extra Date allocation. + * @return {number} The amount of time in ms that the user has been idle. + */ +goog.ui.ActivityMonitor.prototype.getIdleTime = function(opt_now) { + var now = opt_now || goog.now(); + return now - this.lastEventTime_; +}; + + +/** + * Returns the type of the last user event. + * @return {string} event type. + */ +goog.ui.ActivityMonitor.prototype.getLastEventType = function() { + return this.lastEventType_; +}; + + +/** + * Returns the time of the last event + * @return {number} last event time. + */ +goog.ui.ActivityMonitor.prototype.getLastEventTime = function() { + return this.lastEventTime_; +}; diff --git a/closure-library/closure/goog/ui/advancedtooltip.js b/closure-library/closure/goog/ui/advancedtooltip.js new file mode 100644 index 0000000000..579b95f924 --- /dev/null +++ b/closure-library/closure/goog/ui/advancedtooltip.js @@ -0,0 +1,364 @@ +// Copyright 2007 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 Advanced tooltip widget implementation. + * + * @author eae@google.com (Emil A Eklund) + * @see ../demos/advancedtooltip.html + */ + +goog.provide('goog.ui.AdvancedTooltip'); + +goog.require('goog.events'); +goog.require('goog.events.EventType'); +goog.require('goog.math.Box'); +goog.require('goog.math.Coordinate'); +goog.require('goog.style'); +goog.require('goog.ui.Tooltip'); +goog.require('goog.userAgent'); + + + +/** + * Advanced tooltip widget with cursor tracking abilities. Works like a regular + * tooltip but can track the cursor position and direction to determine if the + * tooltip should be dismissed or remain open. + * + * @param {Element|string=} opt_el Element to display tooltip for, either + * element reference or string id. + * @param {?string=} opt_str Text message to display in tooltip. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper. + * @constructor + * @extends {goog.ui.Tooltip} + */ +goog.ui.AdvancedTooltip = function(opt_el, opt_str, opt_domHelper) { + goog.ui.Tooltip.call(this, opt_el, opt_str, opt_domHelper); +}; +goog.inherits(goog.ui.AdvancedTooltip, goog.ui.Tooltip); +goog.tagUnsealableClass(goog.ui.AdvancedTooltip); + + +/** + * Whether to track the cursor and thereby close the tooltip if it moves away + * from the tooltip and keep it open if it moves towards it. + * + * @type {boolean} + * @private + */ +goog.ui.AdvancedTooltip.prototype.cursorTracking_ = false; + + +/** + * Delay in milliseconds before tooltips are hidden if cursor tracking is + * enabled and the cursor is moving away from the tooltip. + * + * @type {number} + * @private + */ +goog.ui.AdvancedTooltip.prototype.cursorTrackingHideDelayMs_ = 100; + + +/** + * Box object representing a margin around the tooltip where the cursor is + * allowed without dismissing the tooltip. + * + * @type {goog.math.Box} + * @private + */ +goog.ui.AdvancedTooltip.prototype.hotSpotPadding_; + + +/** + * Bounding box. + * + * @type {goog.math.Box} + * @private + */ +goog.ui.AdvancedTooltip.prototype.boundingBox_; + + +/** + * Anchor bounding box. + * + * @type {goog.math.Box} + * @private + */ +goog.ui.AdvancedTooltip.prototype.anchorBox_; + + +/** + * Whether the cursor tracking is active. + * + * @type {boolean} + * @private + */ +goog.ui.AdvancedTooltip.prototype.tracking_ = false; + + +/** + * Sets margin around the tooltip where the cursor is allowed without dismissing + * the tooltip. + * + * @param {goog.math.Box=} opt_box The margin around the tooltip. + */ +goog.ui.AdvancedTooltip.prototype.setHotSpotPadding = function(opt_box) { + this.hotSpotPadding_ = opt_box || null; +}; + + +/** + * @return {goog.math.Box} box The margin around the tooltip where the cursor is + * allowed without dismissing the tooltip. + */ +goog.ui.AdvancedTooltip.prototype.getHotSpotPadding = function() { + return this.hotSpotPadding_; +}; + + +/** + * Sets whether to track the cursor and thereby close the tooltip if it moves + * away from the tooltip and keep it open if it moves towards it. + * + * @param {boolean} b Whether to track the cursor. + */ +goog.ui.AdvancedTooltip.prototype.setCursorTracking = function(b) { + this.cursorTracking_ = b; +}; + + +/** + * @return {boolean} Whether to track the cursor and thereby close the tooltip + * if it moves away from the tooltip and keep it open if it moves towards + * it. + */ +goog.ui.AdvancedTooltip.prototype.getCursorTracking = function() { + return this.cursorTracking_; +}; + + +/** + * Sets delay in milliseconds before tooltips are hidden if cursor tracking is + * enabled and the cursor is moving away from the tooltip. + * + * @param {number} delay The delay in milliseconds. + */ +goog.ui.AdvancedTooltip.prototype.setCursorTrackingHideDelayMs = function( + delay) { + this.cursorTrackingHideDelayMs_ = delay; +}; + + +/** + * @return {number} The delay in milliseconds before tooltips are hidden if + * cursor tracking is enabled and the cursor is moving away from the + * tooltip. + */ +goog.ui.AdvancedTooltip.prototype.getCursorTrackingHideDelayMs = function() { + return this.cursorTrackingHideDelayMs_; +}; + + +/** + * Called after the popup is shown. + * @protected + * @override + */ +goog.ui.AdvancedTooltip.prototype.onShow = function() { + goog.ui.AdvancedTooltip.superClass_.onShow.call(this); + + this.boundingBox_ = goog.style.getBounds(this.getElement()).toBox(); + if (this.anchor) { + this.anchorBox_ = goog.style.getBounds(this.anchor).toBox(); + } + + this.tracking_ = this.cursorTracking_; + goog.events.listen( + this.getDomHelper().getDocument(), goog.events.EventType.MOUSEMOVE, + this.handleMouseMove, false, this); +}; + + +/** + * Called after the popup is hidden. + * @protected + * @override + */ +goog.ui.AdvancedTooltip.prototype.onHide = function() { + goog.events.unlisten( + this.getDomHelper().getDocument(), goog.events.EventType.MOUSEMOVE, + this.handleMouseMove, false, this); + + this.boundingBox_ = null; + this.anchorBox_ = null; + this.tracking_ = false; + + goog.ui.AdvancedTooltip.superClass_.onHide.call(this); +}; + + +/** + * Returns true if the mouse is in the tooltip. + * @return {boolean} True if the mouse is in the tooltip. + */ +goog.ui.AdvancedTooltip.prototype.isMouseInTooltip = function() { + return this.isCoordinateInTooltip(this.cursorPosition); +}; + + +/** + * Checks whether the supplied coordinate is inside the tooltip, including + * padding if any. + * @param {goog.math.Coordinate} coord Coordinate being tested. + * @return {boolean} Whether the coord is in the tooltip. + * @override + */ +goog.ui.AdvancedTooltip.prototype.isCoordinateInTooltip = function(coord) { + // Check if coord is inside the bounding box of the tooltip + if (this.hotSpotPadding_) { + var offset = goog.style.getPageOffset(this.getElement()); + var size = goog.style.getSize(this.getElement()); + return offset.x - this.hotSpotPadding_.left <= coord.x && + coord.x <= offset.x + size.width + this.hotSpotPadding_.right && + offset.y - this.hotSpotPadding_.top <= coord.y && + coord.y <= offset.y + size.height + this.hotSpotPadding_.bottom; + } + + return goog.ui.AdvancedTooltip.superClass_.isCoordinateInTooltip.call( + this, coord); +}; + + +/** + * Checks if supplied coordinate is in the tooltip, its triggering anchor, or + * a tooltip that has been triggered by a child of this tooltip. + * Called from handleMouseMove to determine if hide timer should be started, + * and from maybeHide to determine if tooltip should be hidden. + * @param {goog.math.Coordinate} coord Coordinate being tested. + * @return {boolean} Whether coordinate is in the anchor, the tooltip, or any + * tooltip whose anchor is a child of this tooltip. + * @private + */ +goog.ui.AdvancedTooltip.prototype.isCoordinateActive_ = function(coord) { + if ((this.anchorBox_ && this.anchorBox_.contains(coord)) || + this.isCoordinateInTooltip(coord)) { + return true; + } + + // Check if mouse might be in active child element. + var childTooltip = this.getChildTooltip(); + return !!childTooltip && childTooltip.isCoordinateInTooltip(coord); +}; + + +/** + * Called by timer from mouse out handler. Hides tooltip if cursor is still + * outside element and tooltip. + * @param {?Element|undefined} el Anchor when hide timer was started. + * @override + */ +goog.ui.AdvancedTooltip.prototype.maybeHide = function(el) { + this.hideTimer = undefined; + if (el == this.anchor) { + // Check if cursor is inside the bounding box of the tooltip or the element + // that triggered it, or if tooltip is active (possibly due to receiving + // the focus), or if there is a nested tooltip being shown. + if (!this.isCoordinateActive_(this.cursorPosition) && + !this.getActiveElement() && !this.hasActiveChild()) { + // Under certain circumstances gecko fires ghost mouse events with the + // coordinates 0, 0 regardless of the cursors position. + if (goog.userAgent.GECKO && this.cursorPosition.x == 0 && + this.cursorPosition.y == 0) { + return; + } + this.setVisible(false); + } + } +}; + + +/** + * Handler for mouse move events. + * + * @param {goog.events.BrowserEvent} event Event object. + * @protected + * @override + */ +goog.ui.AdvancedTooltip.prototype.handleMouseMove = function(event) { + var startTimer = this.isVisible(); + if (this.boundingBox_) { + var scroll = this.getDomHelper().getDocumentScroll(); + var c = new goog.math.Coordinate( + event.clientX + scroll.x, event.clientY + scroll.y); + if (this.isCoordinateActive_(c)) { + startTimer = false; + } else if (this.tracking_) { + var prevDist = + goog.math.Box.distance(this.boundingBox_, this.cursorPosition); + var currDist = goog.math.Box.distance(this.boundingBox_, c); + startTimer = currDist >= prevDist; + } + } + + if (startTimer) { + this.startHideTimer(); + + // Even though the mouse coordinate is not on the tooltip (or nested child), + // they may have an active element because of a focus event. Don't let + // that prevent us from taking down the tooltip(s) on this mouse move. + this.setActiveElement(null); + var childTooltip = this.getChildTooltip(); + if (childTooltip) { + childTooltip.setActiveElement(null); + } + } else if (this.getState() == goog.ui.Tooltip.State.WAITING_TO_HIDE) { + this.clearHideTimer(); + } + + goog.ui.AdvancedTooltip.superClass_.handleMouseMove.call(this, event); +}; + + +/** + * Handler for mouse over events for the tooltip element. + * + * @param {goog.events.BrowserEvent} event Event object. + * @protected + * @override + */ +goog.ui.AdvancedTooltip.prototype.handleTooltipMouseOver = function(event) { + if (this.getActiveElement() != this.getElement()) { + this.tracking_ = false; + this.setActiveElement(this.getElement()); + } +}; + + +/** + * Override hide delay with cursor tracking hide delay while tracking. + * @return {number} Hide delay to use. + * @override + */ +goog.ui.AdvancedTooltip.prototype.getHideDelayMs = function() { + return this.tracking_ ? this.cursorTrackingHideDelayMs_ : + goog.ui.AdvancedTooltip.base(this, 'getHideDelayMs'); +}; + + +/** + * Forces the recalculation of the hotspot on the next mouse over event. + * @deprecated Not ever necessary to call this function. Hot spot is calculated + * as necessary. + */ +goog.ui.AdvancedTooltip.prototype.resetHotSpot = goog.nullFunction; diff --git a/closure-library/closure/goog/ui/animatedzippy.js b/closure-library/closure/goog/ui/animatedzippy.js new file mode 100644 index 0000000000..6915eceb6a --- /dev/null +++ b/closure-library/closure/goog/ui/animatedzippy.js @@ -0,0 +1,235 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Animated zippy widget implementation. + * + * @author eae@google.com (Emil A Eklund) + * @see ../demos/zippy.html + */ + +goog.provide('goog.ui.AnimatedZippy'); + +goog.require('goog.a11y.aria.Role'); +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.events'); +goog.require('goog.fx.Animation'); +goog.require('goog.fx.Transition'); +goog.require('goog.fx.easing'); +goog.require('goog.ui.Zippy'); +goog.require('goog.ui.ZippyEvent'); + + + +/** + * Zippy widget. Expandable/collapsible container, clicking the header toggles + * the visibility of the content. + * + * @param {Element|string|null} header Header element, either element + * reference, string id or null if no header exists. + * @param {Element|string} content Content element, either element reference or + * string id. + * @param {boolean=} opt_expanded Initial expanded/visibility state. Defaults to + * false. + * @param {goog.dom.DomHelper=} opt_domHelper An optional DOM helper. + * @param {goog.a11y.aria.Role=} opt_role ARIA role, default TAB. + * @constructor + * @extends {goog.ui.Zippy} + */ +goog.ui.AnimatedZippy = function( + header, content, opt_expanded, opt_domHelper, opt_role) { + var domHelper = opt_domHelper || goog.dom.getDomHelper(); + + // Create wrapper element and move content into it. + var elWrapper = + domHelper.createDom(goog.dom.TagName.DIV, {'style': 'overflow:hidden'}); + var elContent = domHelper.getElement(content); + elContent.parentNode.replaceChild(elWrapper, elContent); + elWrapper.appendChild(elContent); + + /** + * Content wrapper, used for animation. + * @type {Element} + * @private + */ + this.elWrapper_ = elWrapper; + + /** + * Reference to animation or null if animation is not active. + * @type {goog.fx.Animation} + * @private + */ + this.anim_ = null; + + // Call constructor of super class. + goog.ui.Zippy.call( + this, header, elContent, opt_expanded, undefined, domHelper, opt_role); + + // Set initial state. + // NOTE: Set the class names as well otherwise animated zippys + // start with empty class names. + var expanded = this.isExpanded(); + this.elWrapper_.style.display = expanded ? '' : 'none'; + this.updateHeaderClassName(expanded); +}; +goog.inherits(goog.ui.AnimatedZippy, goog.ui.Zippy); +goog.tagUnsealableClass(goog.ui.AnimatedZippy); + + +/** + * Constants for event names. + * + * @const + */ +goog.ui.AnimatedZippy.Events = { + // The beginning of the animation when the zippy state toggles. + TOGGLE_ANIMATION_BEGIN: goog.events.getUniqueId('toggleanimationbegin'), + // The end of the animation when the zippy state toggles. + TOGGLE_ANIMATION_END: goog.events.getUniqueId('toggleanimationend') +}; + + +/** + * Duration of expand/collapse animation, in milliseconds. + * @type {number} + */ +goog.ui.AnimatedZippy.prototype.animationDuration = 500; + + +/** + * Acceleration function for expand/collapse animation. + * @type {!Function} + */ +goog.ui.AnimatedZippy.prototype.animationAcceleration = goog.fx.easing.easeOut; + + +/** + * @return {boolean} Whether the zippy is in the process of being expanded or + * collapsed. + */ +goog.ui.AnimatedZippy.prototype.isBusy = function() { + return this.anim_ != null; +}; + + +/** + * Sets expanded state. + * + * @param {boolean} expanded Expanded/visibility state. + * @override + */ +goog.ui.AnimatedZippy.prototype.setExpanded = function(expanded) { + if (this.isExpanded() == expanded && !this.anim_) { + return; + } + + // Reset display property of wrapper to allow content element to be + // measured. + if (this.elWrapper_.style.display == 'none') { + this.elWrapper_.style.display = ''; + } + + // Measure content element. + var h = this.getContentElement().offsetHeight; + + // Stop active animation (if any) and determine starting height. + var startH = 0; + if (this.anim_) { + expanded = this.isExpanded(); + goog.events.removeAll(this.anim_); + this.anim_.stop(false); + + var marginTop = parseInt(this.getContentElement().style.marginTop, 10); + startH = h - Math.abs(marginTop); + } else { + startH = expanded ? 0 : h; + } + + // Updates header class name after the animation has been stopped. + this.updateHeaderClassName(expanded); + + // Set up expand/collapse animation. + this.anim_ = new goog.fx.Animation( + [0, startH], [0, expanded ? h : 0], this.animationDuration, + this.animationAcceleration); + + var events = [ + goog.fx.Transition.EventType.BEGIN, goog.fx.Animation.EventType.ANIMATE, + goog.fx.Transition.EventType.END + ]; + goog.events.listen(this.anim_, events, this.onAnimate_, false, this); + goog.events.listen( + this.anim_, goog.fx.Transition.EventType.BEGIN, + goog.bind(this.onAnimationBegin_, this, expanded)); + goog.events.listen( + this.anim_, goog.fx.Transition.EventType.END, + goog.bind(this.onAnimationCompleted_, this, expanded)); + + // Start animation. + this.anim_.play(false); +}; + + +/** + * Called during animation + * + * @param {goog.events.Event} e The event. + * @private + */ +goog.ui.AnimatedZippy.prototype.onAnimate_ = function(e) { + var contentElement = this.getContentElement(); + var h = contentElement.offsetHeight; + contentElement.style.marginTop = (e.y - h) + 'px'; +}; + + +/** + * Called once the expand/collapse animation has started. + * + * @param {boolean} expanding Expanded/visibility state. + * @private + */ +goog.ui.AnimatedZippy.prototype.onAnimationBegin_ = function(expanding) { + this.dispatchEvent(new goog.ui.ZippyEvent( + goog.ui.AnimatedZippy.Events.TOGGLE_ANIMATION_BEGIN, this, expanding)); +}; + + +/** + * Called once the expand/collapse animation has completed. + * + * @param {boolean} expanded Expanded/visibility state. + * @private + */ +goog.ui.AnimatedZippy.prototype.onAnimationCompleted_ = function(expanded) { + // Fix wrong end position if the content has changed during the animation. + if (expanded) { + this.getContentElement().style.marginTop = '0'; + } + + goog.events.removeAll(/** @type {!goog.fx.Animation} */ (this.anim_)); + this.setExpandedInternal(expanded); + this.anim_ = null; + + if (!expanded) { + this.elWrapper_.style.display = 'none'; + } + + // Fire toggle event. + this.dispatchEvent( + new goog.ui.ZippyEvent(goog.ui.Zippy.Events.TOGGLE, this, expanded)); + this.dispatchEvent(new goog.ui.ZippyEvent( + goog.ui.AnimatedZippy.Events.TOGGLE_ANIMATION_END, this, expanded)); +}; diff --git a/closure-library/closure/goog/ui/attachablemenu.js b/closure-library/closure/goog/ui/attachablemenu.js new file mode 100644 index 0000000000..e51ec6caf7 --- /dev/null +++ b/closure-library/closure/goog/ui/attachablemenu.js @@ -0,0 +1,477 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Definition of the AttachableMenu class. + * + */ + +goog.provide('goog.ui.AttachableMenu'); + +goog.require('goog.a11y.aria'); +goog.require('goog.a11y.aria.State'); +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.classlist'); +goog.require('goog.events.Event'); +goog.require('goog.events.KeyCodes'); +goog.require('goog.string'); +goog.require('goog.style'); +goog.require('goog.ui.ItemEvent'); +goog.require('goog.ui.MenuBase'); +goog.require('goog.ui.PopupBase'); +goog.require('goog.userAgent'); + + + +/** + * An implementation of a menu that can attach itself to DOM element that + * are annotated appropriately. + * + * The following attributes are used by the AttachableMenu + * + * menu-item - Should be set on DOM elements that function as items in the + * menu that can be selected. + * classNameSelected - A class that will be added to the element's class names + * when the item is selected via keyboard or mouse. + * + * @param {Element=} opt_element A DOM element for the popup. + * @constructor + * @extends {goog.ui.MenuBase} + * @deprecated Use goog.ui.PopupMenu. + * @final + */ +goog.ui.AttachableMenu = function(opt_element) { + goog.ui.MenuBase.call(this, opt_element); +}; +goog.inherits(goog.ui.AttachableMenu, goog.ui.MenuBase); +goog.tagUnsealableClass(goog.ui.AttachableMenu); + + +/** + * The currently selected element (mouse was moved over it or keyboard arrows) + * @type {HTMLElement} + * @private + */ +goog.ui.AttachableMenu.prototype.selectedElement_ = null; + + +/** + * Class name to append to a menu item's class when it's selected + * @type {string} + * @private + */ +goog.ui.AttachableMenu.prototype.itemClassName_ = 'menu-item'; + + +/** + * Class name to append to a menu item's class when it's selected + * @type {string} + * @private + */ +goog.ui.AttachableMenu.prototype.selectedItemClassName_ = 'menu-item-selected'; + + +/** + * Keep track of when the last key was pressed so that a keydown-scroll doesn't + * trigger a mouseover event + * @type {number} + * @private + */ +goog.ui.AttachableMenu.prototype.lastKeyDown_ = goog.now(); + + +/** @override */ +goog.ui.AttachableMenu.prototype.disposeInternal = function() { + goog.ui.AttachableMenu.superClass_.disposeInternal.call(this); + this.selectedElement_ = null; +}; + + +/** + * Sets the class name to use for menu items + * + * @return {string} The class name to use for items. + */ +goog.ui.AttachableMenu.prototype.getItemClassName = function() { + return this.itemClassName_; +}; + + +/** + * Sets the class name to use for menu items + * + * @param {string} name The class name to use for items. + */ +goog.ui.AttachableMenu.prototype.setItemClassName = function(name) { + this.itemClassName_ = name; +}; + + +/** + * Sets the class name to use for selected menu items + * todo(user) - reevaluate if we can simulate pseudo classes in IE + * + * @return {string} The class name to use for selected items. + */ +goog.ui.AttachableMenu.prototype.getSelectedItemClassName = function() { + return this.selectedItemClassName_; +}; + + +/** + * Sets the class name to use for selected menu items + * todo(user) - reevaluate if we can simulate pseudo classes in IE + * + * @param {string} name The class name to use for selected items. + */ +goog.ui.AttachableMenu.prototype.setSelectedItemClassName = function(name) { + this.selectedItemClassName_ = name; +}; + + +/** + * Returns the selected item + * + * @return {Element} The item selected or null if no item is selected. + * @override + */ +goog.ui.AttachableMenu.prototype.getSelectedItem = function() { + return this.selectedElement_; +}; + + +/** @override */ +goog.ui.AttachableMenu.prototype.setSelectedItem = function(obj) { + var elt = /** @type {HTMLElement} */ (obj); + if (this.selectedElement_) { + goog.dom.classlist.remove( + this.selectedElement_, this.selectedItemClassName_); + } + + this.selectedElement_ = elt; + + var el = /** @type {HTMLElement} */ (this.getElement()); + goog.asserts.assert(el, 'The attachable menu DOM element cannot be null.'); + if (this.selectedElement_) { + goog.dom.classlist.add(this.selectedElement_, this.selectedItemClassName_); + + if (elt.id) { + // Update activedescendant to reflect the new selection. ARIA roles for + // menu and menuitem can be set statically (through Soy templates, for + // example) whereas this needs to be updated as the selection changes. + goog.a11y.aria.setState( + el, goog.a11y.aria.State.ACTIVEDESCENDANT, elt.id); + } + + var top = this.selectedElement_.offsetTop; + var height = this.selectedElement_.offsetHeight; + var scrollTop = el.scrollTop; + var scrollHeight = el.offsetHeight; + + // If the menu is scrollable this scrolls the selected item into view + // (this has no effect when the menu doesn't scroll) + if (top < scrollTop) { + el.scrollTop = top; + } else if (top + height > scrollTop + scrollHeight) { + el.scrollTop = top + height - scrollHeight; + } + } else { + // Clear off activedescendant to reflect no selection. + goog.a11y.aria.setState(el, goog.a11y.aria.State.ACTIVEDESCENDANT, ''); + } +}; + + +/** @override */ +goog.ui.AttachableMenu.prototype.showPopupElement = function() { + // The scroll position cannot be set for hidden (display: none) elements in + // gecko browsers. + var el = /** @type {Element} */ (this.getElement()); + goog.style.setElementShown(el, true); + el.scrollTop = 0; + el.style.visibility = 'visible'; +}; + + +/** + * Called after the menu is shown. + * @override + * @protected + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.AttachableMenu.prototype.onShow = function() { + goog.ui.AttachableMenu.superClass_.onShow.call(this); + + // In IE, focusing the menu causes weird scrolling to happen. Focusing the + // first child makes the scroll behavior better, and the key handling still + // works. In FF, focusing the first child causes us to lose key events, so we + // still focus the menu. + var el = this.getElement(); + goog.userAgent.IE ? el.firstChild.focus() : el.focus(); +}; + + +/** + * Returns the next or previous item. Used for up/down arrows. + * + * @param {boolean} prev True to go to the previous element instead of next. + * @return {Element} The next or previous element. + * @protected + */ +goog.ui.AttachableMenu.prototype.getNextPrevItem = function(prev) { + // first find the index of the next element + var elements = this.getElement().getElementsByTagName('*'); + var elementCount = elements.length; + var index; + // if there is a selected element, find its index and then inc/dec by one + if (this.selectedElement_) { + for (var i = 0; i < elementCount; i++) { + if (elements[i] == this.selectedElement_) { + index = prev ? i - 1 : i + 1; + break; + } + } + } + + // if no selected element, start from beginning or end + if (!goog.isDef(index)) { + index = prev ? elementCount - 1 : 0; + } + + // iterate forward or backwards through the elements finding the next + // menu item + for (var i = 0; i < elementCount; i++) { + var multiplier = prev ? -1 : 1; + var nextIndex = index + (multiplier * i) % elementCount; + + // if overflowed/underflowed, wrap around + if (nextIndex < 0) { + nextIndex += elementCount; + } else if (nextIndex >= elementCount) { + nextIndex -= elementCount; + } + + if (this.isMenuItem_(elements[nextIndex])) { + return elements[nextIndex]; + } + } + return null; +}; + + +/** + * Mouse over handler for the menu. + * @param {goog.events.Event} e The event object. + * @protected + * @override + */ +goog.ui.AttachableMenu.prototype.onMouseOver = function(e) { + var eltItem = this.getAncestorMenuItem_(/** @type {Element} */ (e.target)); + if (eltItem == null) { + return; + } + + // Stop the keydown triggering a mouseover in FF. + if (goog.now() - this.lastKeyDown_ > goog.ui.PopupBase.DEBOUNCE_DELAY_MS) { + this.setSelectedItem(eltItem); + } +}; + + +/** + * Mouse out handler for the menu. + * @param {goog.events.Event} e The event object. + * @protected + * @override + */ +goog.ui.AttachableMenu.prototype.onMouseOut = function(e) { + var eltItem = this.getAncestorMenuItem_(/** @type {Element} */ (e.target)); + if (eltItem == null) { + return; + } + + // Stop the keydown triggering a mouseout in FF. + if (goog.now() - this.lastKeyDown_ > goog.ui.PopupBase.DEBOUNCE_DELAY_MS) { + this.setSelectedItem(null); + } +}; + + +/** + * Mouse down handler for the menu. Prevents default to avoid text selection. + * @param {!goog.events.Event} e The event object. + * @protected + * @override + */ +goog.ui.AttachableMenu.prototype.onMouseDown = goog.events.Event.preventDefault; + + +/** + * Mouse up handler for the menu. + * @param {goog.events.Event} e The event object. + * @protected + * @override + */ +goog.ui.AttachableMenu.prototype.onMouseUp = function(e) { + var eltItem = this.getAncestorMenuItem_(/** @type {Element} */ (e.target)); + if (eltItem == null) { + return; + } + this.setVisible(false); + this.onItemSelected_(eltItem); +}; + + +/** + * Key down handler for the menu. + * @param {goog.events.KeyEvent} e The event object. + * @protected + * @override + */ +goog.ui.AttachableMenu.prototype.onKeyDown = function(e) { + switch (e.keyCode) { + case goog.events.KeyCodes.DOWN: + this.setSelectedItem(this.getNextPrevItem(false)); + this.lastKeyDown_ = goog.now(); + break; + case goog.events.KeyCodes.UP: + this.setSelectedItem(this.getNextPrevItem(true)); + this.lastKeyDown_ = goog.now(); + break; + case goog.events.KeyCodes.ENTER: + if (this.selectedElement_) { + this.onItemSelected_(); + this.setVisible(false); + } + break; + case goog.events.KeyCodes.ESC: + this.setVisible(false); + break; + default: + if (e.charCode) { + var charStr = String.fromCharCode(e.charCode); + this.selectByName_(charStr, 1, true); + } + break; + } + // Prevent the browser's default keydown behaviour when the menu is open, + // e.g. keyboard scrolling. + e.preventDefault(); + + // Stop propagation to prevent application level keyboard shortcuts from + // firing. + e.stopPropagation(); + + this.dispatchEvent(e); +}; + + +/** + * Find an item that has the given prefix and select it. + * + * @param {string} prefix The entered prefix, so far. + * @param {number=} opt_direction 1 to search forward from the selection + * (default), -1 to search backward (e.g. to go to the previous match). + * @param {boolean=} opt_skip True if should skip the current selection, + * unless no other item has the given prefix. + * @private + */ +goog.ui.AttachableMenu.prototype.selectByName_ = function( + prefix, opt_direction, opt_skip) { + var elements = this.getElement().getElementsByTagName('*'); + var elementCount = elements.length; + var index; + + if (elementCount == 0) { + return; + } + + if (!this.selectedElement_ || + (index = goog.array.indexOf(elements, this.selectedElement_)) == -1) { + // no selection or selection isn't known => start at the beginning + index = 0; + } + + var start = index; + var re = new RegExp('^' + goog.string.regExpEscape(prefix), 'i'); + var skip = opt_skip && this.selectedElement_; + var dir = opt_direction || 1; + + do { + if (elements[index] != skip && this.isMenuItem_(elements[index])) { + var name = goog.dom.getTextContent(elements[index]); + if (name.match(re)) { + break; + } + } + index += dir; + if (index == elementCount) { + index = 0; + } else if (index < 0) { + index = elementCount - 1; + } + } while (index != start); + + if (this.selectedElement_ != elements[index]) { + this.setSelectedItem(elements[index]); + } +}; + + +/** + * Dispatch an ITEM_ACTION event when an item is selected + * @param {Object=} opt_item Item selected. + * @private + */ +goog.ui.AttachableMenu.prototype.onItemSelected_ = function(opt_item) { + this.dispatchEvent( + new goog.ui.ItemEvent( + goog.ui.MenuBase.Events.ITEM_ACTION, this, + opt_item || this.selectedElement_)); +}; + + +/** + * Returns whether the specified element is a menu item. + * @param {Element} elt The element to find a menu item ancestor of. + * @return {boolean} Whether the specified element is a menu item. + * @private + */ +goog.ui.AttachableMenu.prototype.isMenuItem_ = function(elt) { + return !!elt && goog.dom.classlist.contains(elt, this.itemClassName_); +}; + + +/** + * Returns the menu-item scoping the specified element, or null if there is + * none. + * @param {Element|undefined} elt The element to find a menu item ancestor of. + * @return {Element} The menu-item scoping the specified element, or null if + * there is none. + * @private + */ +goog.ui.AttachableMenu.prototype.getAncestorMenuItem_ = function(elt) { + if (elt) { + var ownerDocumentBody = goog.dom.getOwnerDocument(elt).body; + while (elt != null && elt != ownerDocumentBody) { + if (this.isMenuItem_(elt)) { + return elt; + } + elt = /** @type {Element} */ (elt.parentNode); + } + } + return null; +}; diff --git a/closure-library/closure/goog/ui/bidiinput.js b/closure-library/closure/goog/ui/bidiinput.js new file mode 100644 index 0000000000..ced609756d --- /dev/null +++ b/closure-library/closure/goog/ui/bidiinput.js @@ -0,0 +1,178 @@ +// Copyright 2007 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 Component for an input field with bidi direction automatic + * detection. The input element directionality is automatically set according + * to the contents (value) of the element. + * + * @see ../demos/bidiinput.html + */ + + +goog.provide('goog.ui.BidiInput'); + + +goog.require('goog.dom'); +goog.require('goog.dom.InputType'); +goog.require('goog.dom.TagName'); +goog.require('goog.events'); +goog.require('goog.events.InputHandler'); +goog.require('goog.i18n.bidi'); +goog.require('goog.ui.Component'); + + + +/** + * Default implementation of BidiInput. + * + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper. + * @constructor + * @extends {goog.ui.Component} + */ +goog.ui.BidiInput = function(opt_domHelper) { + goog.ui.Component.call(this, opt_domHelper); +}; +goog.inherits(goog.ui.BidiInput, goog.ui.Component); +goog.tagUnsealableClass(goog.ui.BidiInput); + + +/** + * The input handler that provides the input event. + * @type {goog.events.InputHandler?} + * @private + */ +goog.ui.BidiInput.prototype.inputHandler_ = null; + + +/** + * Decorates the given HTML element as a BidiInput. The HTML element can be an + * input element with type='text', a textarea element, or any contenteditable. + * Overrides {@link goog.ui.Component#decorateInternal}. Considered protected. + * @param {Element} element Element to decorate. + * @protected + * @override + */ +goog.ui.BidiInput.prototype.decorateInternal = function(element) { + goog.ui.BidiInput.superClass_.decorateInternal.call(this, element); + this.init_(); +}; + + +/** + * Creates the element for the text input. + * @protected + * @override + */ +goog.ui.BidiInput.prototype.createDom = function() { + this.setElementInternal( + this.getDomHelper().createDom( + goog.dom.TagName.INPUT, {'type': goog.dom.InputType.TEXT})); + this.init_(); +}; + + +/** + * Initializes the events and initial text direction. + * Called from either decorate or createDom, after the input field has + * been created. + * @private + */ +goog.ui.BidiInput.prototype.init_ = function() { + // Set initial direction by current text + this.setDirection_(); + + // Listen to value change events + this.inputHandler_ = new goog.events.InputHandler(this.getElement()); + goog.events.listen( + this.inputHandler_, goog.events.InputHandler.EventType.INPUT, + this.setDirection_, false, this); +}; + + +/** + * Set the direction of the input element based on the current value. If the + * value does not have any strongly directional characters, remove the dir + * attribute so that the direction is inherited instead. + * This method is called when the user changes the input element value, or + * when a program changes the value using + * {@link goog.ui.BidiInput#setValue} + * @private + */ +goog.ui.BidiInput.prototype.setDirection_ = function() { + var element = this.getElement(); + if (element) { + var text = this.getValue(); + goog.i18n.bidi.setElementDirByTextDirectionality(element, text); + } +}; + + +/** + * Returns the direction of the input element. + * @return {?string} Return 'rtl' for right-to-left text, + * 'ltr' for left-to-right text, or null if the value itself is not + * enough to determine directionality (e.g. an empty value), and the + * direction is inherited from a parent element (typically the body + * element). + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.BidiInput.prototype.getDirection = function() { + var dir = this.getElement().dir; + if (dir == '') { + dir = null; + } + return dir; +}; + + +/** + * Sets the value of the underlying input field, and sets the direction + * according to the given value. + * @param {string} value The Value to set in the underlying input field. + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.BidiInput.prototype.setValue = function(value) { + var element = this.getElement(); + if (goog.isDefAndNotNull(element.value)) { + element.value = value; + } else { + goog.dom.setTextContent(element, value); + } + this.setDirection_(); +}; + + +/** + * Returns the value of the underlying input field. + * @return {string} Value of the underlying input field. + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.BidiInput.prototype.getValue = function() { + var element = this.getElement(); + return goog.isDefAndNotNull(element.value) ? + element.value : + goog.dom.getRawTextContent(element); +}; + + +/** @override */ +goog.ui.BidiInput.prototype.disposeInternal = function() { + if (this.inputHandler_) { + goog.events.removeAll(this.inputHandler_); + this.inputHandler_.dispose(); + this.inputHandler_ = null; + } + goog.ui.BidiInput.base(this, 'disposeInternal'); +}; diff --git a/closure-library/closure/goog/ui/bubble.js b/closure-library/closure/goog/ui/bubble.js new file mode 100644 index 0000000000..b78de553cb --- /dev/null +++ b/closure-library/closure/goog/ui/bubble.js @@ -0,0 +1,498 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Definition of the Bubble class. + * + * + * @see ../demos/bubble.html + * + * TODO: support decoration and addChild + */ + +goog.provide('goog.ui.Bubble'); + +goog.require('goog.Timer'); +goog.require('goog.dom.safe'); +goog.require('goog.events'); +goog.require('goog.events.EventType'); +goog.require('goog.html.SafeHtml'); +goog.require('goog.math.Box'); +goog.require('goog.positioning'); +goog.require('goog.positioning.AbsolutePosition'); +goog.require('goog.positioning.AnchoredPosition'); +goog.require('goog.positioning.Corner'); +goog.require('goog.positioning.CornerBit'); +goog.require('goog.string.Const'); +goog.require('goog.style'); +goog.require('goog.ui.Component'); +goog.require('goog.ui.Popup'); + + +goog.scope(function() { +var SafeHtml = goog.html.SafeHtml; + + + +/** + * The Bubble provides a general purpose bubble implementation that can be + * anchored to a particular element and displayed for a period of time. + * + * @param {string|!goog.html.SafeHtml|?Element} message Message or an element + * to display inside the bubble. Strings are treated as plain-text and will + * be HTML escaped. + * @param {Object=} opt_config The configuration + * for the bubble. If not specified, the default configuration will be + * used. {@see goog.ui.Bubble.defaultConfig}. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper. + * @constructor + * @extends {goog.ui.Component} + */ +goog.ui.Bubble = function(message, opt_config, opt_domHelper) { + goog.ui.Component.call(this, opt_domHelper); + + if (goog.isString(message)) { + message = goog.html.SafeHtml.htmlEscape(message); + } + + /** + * The HTML string or element to display inside the bubble. + * + * @type {!goog.html.SafeHtml|Element} + * @private + */ + this.message_ = message; + + /** + * The Popup element used to position and display the bubble. + * + * @type {goog.ui.Popup} + * @private + */ + this.popup_ = new goog.ui.Popup(); + + /** + * Configuration map that contains bubble's UI elements. + * + * @type {Object} + * @private + */ + this.config_ = opt_config || goog.ui.Bubble.defaultConfig; + + /** + * Id of the close button for this bubble. + * + * @type {string} + * @private + */ + this.closeButtonId_ = this.makeId('cb'); + + /** + * Id of the div for the embedded element. + * + * @type {string} + * @private + */ + this.messageId_ = this.makeId('mi'); + +}; +goog.inherits(goog.ui.Bubble, goog.ui.Component); +goog.tagUnsealableClass(goog.ui.Bubble); + + +/** + * In milliseconds, timeout after which the button auto-hides. Null means + * infinite. + * @type {?number} + * @private + */ +goog.ui.Bubble.prototype.timeout_ = null; + + +/** + * Key returned by the bubble timer. + * @type {?number} + * @private + */ +goog.ui.Bubble.prototype.timerId_ = 0; + + +/** + * Key returned by the listen function for the close button. + * @type {goog.events.Key} + * @private + */ +goog.ui.Bubble.prototype.listener_ = null; + + + +/** @override */ +goog.ui.Bubble.prototype.createDom = function() { + goog.ui.Bubble.superClass_.createDom.call(this); + + var element = this.getElement(); + element.style.position = 'absolute'; + element.style.visibility = 'hidden'; + + this.popup_.setElement(element); +}; + + +/** + * Attaches the bubble to an anchor element. Computes the positioning and + * orientation of the bubble. + * + * @param {Element} anchorElement The element to which we are attaching. + */ +goog.ui.Bubble.prototype.attach = function(anchorElement) { + this.setAnchoredPosition_( + anchorElement, this.computePinnedCorner_(anchorElement)); +}; + + +/** + * Sets the corner of the bubble to used in the positioning algorithm. + * + * @param {goog.positioning.Corner} corner The bubble corner used for + * positioning constants. + */ +goog.ui.Bubble.prototype.setPinnedCorner = function(corner) { + this.popup_.setPinnedCorner(corner); +}; + + +/** + * Sets the position of the bubble. Pass null for corner in AnchoredPosition + * for corner to be computed automatically. + * + * @param {goog.positioning.AbstractPosition} position The position of the + * bubble. + */ +goog.ui.Bubble.prototype.setPosition = function(position) { + if (position instanceof goog.positioning.AbsolutePosition) { + this.popup_.setPosition(position); + } else if (position instanceof goog.positioning.AnchoredPosition) { + this.setAnchoredPosition_(position.element, position.corner); + } else { + throw new Error('Bubble only supports absolute and anchored positions!'); + } +}; + + +/** + * Sets the timeout after which bubble hides itself. + * + * @param {number} timeout Timeout of the bubble. + */ +goog.ui.Bubble.prototype.setTimeout = function(timeout) { + this.timeout_ = timeout; +}; + + +/** + * Sets whether the bubble should be automatically hidden whenever user clicks + * outside the bubble element. + * + * @param {boolean} autoHide Whether to hide if user clicks outside the bubble. + */ +goog.ui.Bubble.prototype.setAutoHide = function(autoHide) { + this.popup_.setAutoHide(autoHide); +}; + + +/** + * Sets whether the bubble should be visible. + * + * @param {boolean} visible Desired visibility state. + */ +goog.ui.Bubble.prototype.setVisible = function(visible) { + if (visible && !this.popup_.isVisible()) { + this.configureElement_(); + } + this.popup_.setVisible(visible); + if (!this.popup_.isVisible()) { + this.unconfigureElement_(); + } +}; + + +/** + * @return {boolean} Whether the bubble is visible. + */ +goog.ui.Bubble.prototype.isVisible = function() { + return this.popup_.isVisible(); +}; + + +/** @override */ +goog.ui.Bubble.prototype.disposeInternal = function() { + this.unconfigureElement_(); + this.popup_.dispose(); + this.popup_ = null; + goog.ui.Bubble.superClass_.disposeInternal.call(this); +}; + + +/** + * Creates element's contents and configures all timers. This is called on + * setVisible(true). + * @private + */ +goog.ui.Bubble.prototype.configureElement_ = function() { + if (!this.isInDocument()) { + throw new Error('You must render the bubble before showing it!'); + } + + var element = this.getElement(); + var corner = this.popup_.getPinnedCorner(); + goog.dom.safe.setInnerHtml( + /** @type {!Element} */ (element), this.computeHtmlForCorner_(corner)); + + if (!(this.message_ instanceof SafeHtml)) { + var messageDiv = this.getDomHelper().getElement(this.messageId_); + this.getDomHelper().appendChild(messageDiv, this.message_); + } + var closeButton = this.getDomHelper().getElement(this.closeButtonId_); + this.listener_ = goog.events.listen( + closeButton, goog.events.EventType.CLICK, this.hideBubble_, false, this); + + if (this.timeout_) { + this.timerId_ = goog.Timer.callOnce(this.hideBubble_, this.timeout_, this); + } +}; + + +/** + * Gets rid of the element's contents and all associated timers and listeners. + * This is called on dispose as well as on setVisible(false). + * @private + */ +goog.ui.Bubble.prototype.unconfigureElement_ = function() { + if (this.listener_) { + goog.events.unlistenByKey(this.listener_); + this.listener_ = null; + } + if (this.timerId_) { + goog.Timer.clear(this.timerId_); + this.timerId_ = null; + } + + var element = this.getElement(); + if (element) { + this.getDomHelper().removeChildren(element); + goog.dom.safe.setInnerHtml(element, goog.html.SafeHtml.EMPTY); + } +}; + + +/** + * Computes bubble position based on anchored element. + * + * @param {Element} anchorElement The element to which we are attaching. + * @param {goog.positioning.Corner} corner The bubble corner used for + * positioning. + * @private + */ +goog.ui.Bubble.prototype.setAnchoredPosition_ = function( + anchorElement, corner) { + this.popup_.setPinnedCorner(corner); + var margin = this.createMarginForCorner_(corner); + this.popup_.setMargin(margin); + var anchorCorner = goog.positioning.flipCorner(corner); + this.popup_.setPosition( + new goog.positioning.AnchoredPosition(anchorElement, anchorCorner)); +}; + + +/** + * Hides the bubble. This is called asynchronously by timer of event processor + * for the mouse click on the close button. + * @private + */ +goog.ui.Bubble.prototype.hideBubble_ = function() { + this.setVisible(false); +}; + + +/** + * Returns an AnchoredPosition that will position the bubble optimally + * given the position of the anchor element and the size of the viewport. + * + * @param {Element} anchorElement The element to which the bubble is attached. + * @return {!goog.positioning.AnchoredPosition} The AnchoredPosition + * to give to {@link #setPosition}. + */ +goog.ui.Bubble.prototype.getComputedAnchoredPosition = function(anchorElement) { + return new goog.positioning.AnchoredPosition( + anchorElement, this.computePinnedCorner_(anchorElement)); +}; + + +/** + * Computes the pinned corner for the bubble. + * @param {Element} anchorElement The element to which the button is attached. + * @return {goog.positioning.Corner} The pinned corner. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.Bubble.prototype.computePinnedCorner_ = function(anchorElement) { + var doc = this.getDomHelper().getOwnerDocument(anchorElement); + var viewportElement = goog.style.getClientViewportElement(doc); + var viewportWidth = viewportElement.offsetWidth; + var viewportHeight = viewportElement.offsetHeight; + var anchorElementOffset = goog.style.getPageOffset(anchorElement); + var anchorElementSize = goog.style.getSize(anchorElement); + var anchorType = 0; + // right margin or left? + if (viewportWidth - anchorElementOffset.x - anchorElementSize.width > + anchorElementOffset.x) { + anchorType += 1; + } + // attaches to the top or to the bottom? + if (viewportHeight - anchorElementOffset.y - anchorElementSize.height > + anchorElementOffset.y) { + anchorType += 2; + } + return goog.ui.Bubble.corners_[anchorType]; +}; + + +/** + * Computes the right offset for a given bubble corner + * and creates a margin element for it. This is done to have the + * button anchor element on its frame rather than on the corner. + * @param {goog.positioning.Corner} corner The corner. + * @return {!goog.math.Box} the computed margin. Only left or right fields are + * non-zero, but they may be negative. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.Bubble.prototype.createMarginForCorner_ = function(corner) { + var margin = new goog.math.Box(0, 0, 0, 0); + if (corner & goog.positioning.CornerBit.RIGHT) { + margin.right -= this.config_.marginShift; + } else { + margin.left -= this.config_.marginShift; + } + return margin; +}; + + +/** + * Computes the HTML string for a given bubble orientation. + * @param {goog.positioning.Corner} corner The corner. + * @return {!goog.html.SafeHtml} The HTML string to place inside the + * bubble's popup. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.Bubble.prototype.computeHtmlForCorner_ = function(corner) { + var bubbleTopClass; + var bubbleBottomClass; + switch (corner) { + case goog.positioning.Corner.TOP_LEFT: + bubbleTopClass = this.config_.cssBubbleTopLeftAnchor; + bubbleBottomClass = this.config_.cssBubbleBottomNoAnchor; + break; + case goog.positioning.Corner.TOP_RIGHT: + bubbleTopClass = this.config_.cssBubbleTopRightAnchor; + bubbleBottomClass = this.config_.cssBubbleBottomNoAnchor; + break; + case goog.positioning.Corner.BOTTOM_LEFT: + bubbleTopClass = this.config_.cssBubbleTopNoAnchor; + bubbleBottomClass = this.config_.cssBubbleBottomLeftAnchor; + break; + case goog.positioning.Corner.BOTTOM_RIGHT: + bubbleTopClass = this.config_.cssBubbleTopNoAnchor; + bubbleBottomClass = this.config_.cssBubbleBottomRightAnchor; + break; + default: + throw new Error('This corner type is not supported by bubble!'); + } + var message = null; + if (this.message_ instanceof SafeHtml) { + message = this.message_; + } else { + message = SafeHtml.create('div', {'id': this.messageId_}); + } + + var tableRows = goog.html.SafeHtml.concat( + SafeHtml.create( + 'tr', {}, + SafeHtml.create('td', {'colspan': 4, 'class': bubbleTopClass})), + SafeHtml.create( + 'tr', {}, + SafeHtml.concat( + SafeHtml.create('td', {'class': this.config_.cssBubbleLeft}), + SafeHtml.create( + 'td', { + 'class': this.config_.cssBubbleFont, + 'style': + goog.string.Const.from('padding:0 4px;background:white') + }, + message), + SafeHtml.create('td', { + 'id': this.closeButtonId_, + 'class': this.config_.cssCloseButton + }), + SafeHtml.create('td', {'class': this.config_.cssBubbleRight}))), + SafeHtml.create( + 'tr', {}, + SafeHtml.create('td', {'colspan': 4, 'class': bubbleBottomClass}))); + + return SafeHtml.create( + 'table', { + 'border': 0, + 'cellspacing': 0, + 'cellpadding': 0, + 'width': this.config_.bubbleWidth, + 'style': goog.string.Const.from('z-index:1') + }, + tableRows); +}; + + +/** + * A default configuration for the bubble. + * + * @type {Object} + */ +goog.ui.Bubble.defaultConfig = { + bubbleWidth: 147, + marginShift: 60, + cssBubbleFont: goog.getCssName('goog-bubble-font'), + cssCloseButton: goog.getCssName('goog-bubble-close-button'), + cssBubbleTopRightAnchor: goog.getCssName('goog-bubble-top-right-anchor'), + cssBubbleTopLeftAnchor: goog.getCssName('goog-bubble-top-left-anchor'), + cssBubbleTopNoAnchor: goog.getCssName('goog-bubble-top-no-anchor'), + cssBubbleBottomRightAnchor: + goog.getCssName('goog-bubble-bottom-right-anchor'), + cssBubbleBottomLeftAnchor: goog.getCssName('goog-bubble-bottom-left-anchor'), + cssBubbleBottomNoAnchor: goog.getCssName('goog-bubble-bottom-no-anchor'), + cssBubbleLeft: goog.getCssName('goog-bubble-left'), + cssBubbleRight: goog.getCssName('goog-bubble-right') +}; + + +/** + * An auxiliary array optimizing the corner computation. + * + * @type {Array} + * @private + */ +goog.ui.Bubble.corners_ = [ + goog.positioning.Corner.BOTTOM_RIGHT, goog.positioning.Corner.BOTTOM_LEFT, + goog.positioning.Corner.TOP_RIGHT, goog.positioning.Corner.TOP_LEFT +]; +}); // goog.scope diff --git a/closure-library/closure/goog/ui/button.js b/closure-library/closure/goog/ui/button.js new file mode 100644 index 0000000000..afb80e69b4 --- /dev/null +++ b/closure-library/closure/goog/ui/button.js @@ -0,0 +1,215 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A button control. This implementation extends {@link + * goog.ui.Control}. + * + * @author attila@google.com (Attila Bodis) + * @see ../demos/button.html + */ + +goog.provide('goog.ui.Button'); +goog.provide('goog.ui.Button.Side'); + +goog.require('goog.events.EventType'); +goog.require('goog.events.KeyCodes'); +goog.require('goog.events.KeyHandler'); +goog.require('goog.ui.ButtonRenderer'); +goog.require('goog.ui.ButtonSide'); +goog.require('goog.ui.Component'); +goog.require('goog.ui.Control'); +goog.require('goog.ui.NativeButtonRenderer'); +goog.require('goog.ui.registry'); + + + +/** + * A button control, rendered as a native browser button by default. + * + * @param {goog.ui.ControlContent=} opt_content Text caption or existing DOM + * structure to display as the button's caption (if any). + * @param {goog.ui.ButtonRenderer=} opt_renderer Renderer used to render or + * decorate the button; defaults to {@link goog.ui.NativeButtonRenderer}. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for + * document interaction. + * @constructor + * @extends {goog.ui.Control} + */ +goog.ui.Button = function(opt_content, opt_renderer, opt_domHelper) { + goog.ui.Control.call( + this, opt_content, + opt_renderer || goog.ui.NativeButtonRenderer.getInstance(), + opt_domHelper); +}; +goog.inherits(goog.ui.Button, goog.ui.Control); +goog.tagUnsealableClass(goog.ui.Button); + + +/** + * Constants for button sides, see {@link goog.ui.Button.prototype.setCollapsed} + * for details. Aliased from goog.ui.ButtonSide to support legacy users without + * creating a circular dependency in {@link goog.ui.ButtonRenderer}. + * @enum {number} + * @deprecated use {@link goog.ui.ButtonSide} instead. + */ +goog.ui.Button.Side = goog.ui.ButtonSide; + + +/** + * Value associated with the button. + * @type {*} + * @private + */ +goog.ui.Button.prototype.value_; + + +/** + * Tooltip text for the button, displayed on hover. + * @type {string|undefined} + * @private + */ +goog.ui.Button.prototype.tooltip_; + + +// goog.ui.Button API implementation. + + +/** + * Returns the value associated with the button. + * @return {*} Button value (undefined if none). + */ +goog.ui.Button.prototype.getValue = function() { + return this.value_; +}; + + +/** + * Sets the value associated with the button, and updates its DOM. + * @param {*} value New button value. + */ +goog.ui.Button.prototype.setValue = function(value) { + this.value_ = value; + var renderer = /** @type {!goog.ui.ButtonRenderer} */ (this.getRenderer()); + renderer.setValue(this.getElement(), /** @type {string} */ (value)); +}; + + +/** + * Sets the value associated with the button. Unlike {@link #setValue}, + * doesn't update the button's DOM. Considered protected; to be called only + * by renderer code during element decoration. + * @param {*} value New button value. + * @protected + */ +goog.ui.Button.prototype.setValueInternal = function(value) { + this.value_ = value; +}; + + +/** + * Returns the tooltip for the button. + * @return {string|undefined} Tooltip text (undefined if none). + */ +goog.ui.Button.prototype.getTooltip = function() { + return this.tooltip_; +}; + + +/** + * Sets the tooltip for the button, and updates its DOM. + * @param {string} tooltip New tooltip text. + */ +goog.ui.Button.prototype.setTooltip = function(tooltip) { + this.tooltip_ = tooltip; + this.getRenderer().setTooltip(this.getElement(), tooltip); +}; + + +/** + * Sets the tooltip for the button. Unlike {@link #setTooltip}, doesn't update + * the button's DOM. Considered protected; to be called only by renderer code + * during element decoration. + * @param {string} tooltip New tooltip text. + * @protected + */ +goog.ui.Button.prototype.setTooltipInternal = function(tooltip) { + this.tooltip_ = tooltip; +}; + + +/** + * Collapses the border on one or both sides of the button, allowing it to be + * combined with the adjancent button(s), forming a single UI componenet with + * multiple targets. + * @param {number} sides Bitmap of one or more {@link goog.ui.ButtonSide}s for + * which borders should be collapsed. + */ +goog.ui.Button.prototype.setCollapsed = function(sides) { + this.getRenderer().setCollapsed(this, sides); +}; + + +// goog.ui.Control & goog.ui.Component API implementation. + + +/** @override */ +goog.ui.Button.prototype.disposeInternal = function() { + goog.ui.Button.superClass_.disposeInternal.call(this); + delete this.value_; + delete this.tooltip_; +}; + + +/** @override */ +goog.ui.Button.prototype.enterDocument = function() { + goog.ui.Button.superClass_.enterDocument.call(this); + if (this.isSupportedState(goog.ui.Component.State.FOCUSED)) { + var keyTarget = this.getKeyEventTarget(); + if (keyTarget) { + this.getHandler().listen( + keyTarget, goog.events.EventType.KEYUP, this.handleKeyEventInternal); + } + } +}; + + +/** + * Attempts to handle a keyboard event; returns true if the event was handled, + * false otherwise. If the button is enabled and the Enter/Space key was + * pressed, handles the event by dispatching an `ACTION` event, + * and returns true. Overrides {@link goog.ui.Control#handleKeyEventInternal}. + * @param {goog.events.KeyEvent} e Key event to handle. + * @return {boolean} Whether the key event was handled. + * @protected + * @override + */ +goog.ui.Button.prototype.handleKeyEventInternal = function(e) { + if (e.keyCode == goog.events.KeyCodes.ENTER && + e.type == goog.events.KeyHandler.EventType.KEY || + e.keyCode == goog.events.KeyCodes.SPACE && + e.type == goog.events.EventType.KEYUP) { + return this.performActionInternal(e); + } + // Return true for space keypress (even though the event is handled on keyup) + // as preventDefault needs to be called up keypress to take effect in IE and + // WebKit. + return e.keyCode == goog.events.KeyCodes.SPACE; +}; + + +// Register a decorator factory function for goog.ui.Buttons. +goog.ui.registry.setDecoratorByClassName( + goog.ui.ButtonRenderer.CSS_CLASS, + function() { return new goog.ui.Button(null); }); diff --git a/closure-library/closure/goog/ui/buttonrenderer.js b/closure-library/closure/goog/ui/buttonrenderer.js new file mode 100644 index 0000000000..418e1808ea --- /dev/null +++ b/closure-library/closure/goog/ui/buttonrenderer.js @@ -0,0 +1,222 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Default renderer for {@link goog.ui.Button}s. + * + * @author attila@google.com (Attila Bodis) + */ + +goog.provide('goog.ui.ButtonRenderer'); + +goog.require('goog.a11y.aria'); +goog.require('goog.a11y.aria.Role'); +goog.require('goog.a11y.aria.State'); +goog.require('goog.asserts'); +goog.require('goog.ui.ButtonSide'); +goog.require('goog.ui.Component'); +goog.require('goog.ui.ControlRenderer'); + +goog.forwardDeclare('goog.ui.Button'); // circular + + + +/** + * Default renderer for {@link goog.ui.Button}s. Extends the superclass with + * the following button-specific API methods: + *
        + *
      • `getValue` - returns the button element's value + *
      • `setValue` - updates the button element to reflect its new value + *
      • `getTooltip` - returns the button element's tooltip text + *
      • `setTooltip` - updates the button element's tooltip text + *
      • `setCollapsed` - removes one or both of the button element's + * borders + *
      + * For alternate renderers, see {@link goog.ui.NativeButtonRenderer}, + * {@link goog.ui.CustomButtonRenderer}, and {@link goog.ui.FlatButtonRenderer}. + * @constructor + * @extends {goog.ui.ControlRenderer} + */ +goog.ui.ButtonRenderer = function() { + goog.ui.ControlRenderer.call(this); +}; +goog.inherits(goog.ui.ButtonRenderer, goog.ui.ControlRenderer); +goog.addSingletonGetter(goog.ui.ButtonRenderer); + + +/** + * Default CSS class to be applied to the root element of components rendered + * by this renderer. + * @type {string} + */ +goog.ui.ButtonRenderer.CSS_CLASS = goog.getCssName('goog-button'); + + +/** + * Returns the ARIA role to be applied to buttons. + * @return {goog.a11y.aria.Role|undefined} ARIA role. + * @override + */ +goog.ui.ButtonRenderer.prototype.getAriaRole = function() { + return goog.a11y.aria.Role.BUTTON; +}; + + +/** + * Updates the button's ARIA (accessibility) state if the button is being + * treated as a checkbox. Also makes sure that attributes which aren't + * supported by buttons aren't being added. + * @param {Element} element Element whose ARIA state is to be updated. + * @param {goog.ui.Component.State} state Component state being enabled or + * disabled. + * @param {boolean} enable Whether the state is being enabled or disabled. + * @protected + * @override + */ +goog.ui.ButtonRenderer.prototype.updateAriaState = function( + element, state, enable) { + switch (state) { + // If button has CHECKED or SELECTED state, assign aria-pressed + case goog.ui.Component.State.SELECTED: + case goog.ui.Component.State.CHECKED: + goog.asserts.assert(element, 'The button DOM element cannot be null.'); + goog.a11y.aria.setState(element, goog.a11y.aria.State.PRESSED, enable); + break; + default: + case goog.ui.Component.State.OPENED: + case goog.ui.Component.State.DISABLED: + goog.ui.ButtonRenderer.base( + this, 'updateAriaState', element, state, enable); + break; + } +}; + + +/** @override */ +goog.ui.ButtonRenderer.prototype.createDom = function(button) { + var element = goog.ui.ButtonRenderer.base(this, 'createDom', button); + this.setTooltip(element, button.getTooltip()); + + var value = button.getValue(); + if (value) { + this.setValue(element, value); + } + + // If this is a toggle button, set ARIA state + if (button.isSupportedState(goog.ui.Component.State.CHECKED)) { + this.updateAriaState( + element, goog.ui.Component.State.CHECKED, button.isChecked()); + } + + return element; +}; + + +/** @override */ +goog.ui.ButtonRenderer.prototype.decorate = function(button, element) { + // The superclass implementation takes care of common attributes; we only + // need to set the value and the tooltip. + element = + goog.ui.ButtonRenderer.superClass_.decorate.call(this, button, element); + + button.setValueInternal(this.getValue(element)); + button.setTooltipInternal(this.getTooltip(element)); + + // If this is a toggle button, set ARIA state + if (button.isSupportedState(goog.ui.Component.State.CHECKED)) { + this.updateAriaState( + element, goog.ui.Component.State.CHECKED, button.isChecked()); + } + + return element; +}; + + +/** + * Takes a button's root element, and returns the value associated with it. + * No-op in the base class. + * @param {Element} element The button's root element. + * @return {string|undefined} The button's value (undefined if none). + */ +goog.ui.ButtonRenderer.prototype.getValue = goog.nullFunction; + + +/** + * Takes a button's root element and a value, and updates the element to reflect + * the new value. No-op in the base class. + * @param {Element} element The button's root element. + * @param {string} value New value. + */ +goog.ui.ButtonRenderer.prototype.setValue = goog.nullFunction; + + +/** + * Takes a button's root element, and returns its tooltip text. + * @param {Element} element The button's root element. + * @return {string|undefined} The tooltip text. + */ +goog.ui.ButtonRenderer.prototype.getTooltip = function(element) { + return element.title; +}; + + +/** + * Takes a button's root element and a tooltip string, and updates the element + * with the new tooltip. + * @param {Element} element The button's root element. + * @param {string} tooltip New tooltip text. + * @protected + */ +goog.ui.ButtonRenderer.prototype.setTooltip = function(element, tooltip) { + if (element) { + // Don't set a title attribute if there isn't a tooltip. Blank title + // attributes can be interpreted incorrectly by screen readers. + if (tooltip) { + element.title = tooltip; + } else { + element.removeAttribute('title'); + } + } +}; + + +/** + * Collapses the border on one or both sides of the button, allowing it to be + * combined with the adjacent button(s), forming a single UI componenet with + * multiple targets. + * @param {goog.ui.Button} button Button to update. + * @param {number} sides Bitmap of one or more {@link goog.ui.ButtonSide}s for + * which borders should be collapsed. + * @protected + */ +goog.ui.ButtonRenderer.prototype.setCollapsed = function(button, sides) { + var isRtl = button.isRightToLeft(); + var collapseLeftClassName = + goog.getCssName(this.getStructuralCssClass(), 'collapse-left'); + var collapseRightClassName = + goog.getCssName(this.getStructuralCssClass(), 'collapse-right'); + + button.enableClassName( + isRtl ? collapseRightClassName : collapseLeftClassName, + !!(sides & goog.ui.ButtonSide.START)); + button.enableClassName( + isRtl ? collapseLeftClassName : collapseRightClassName, + !!(sides & goog.ui.ButtonSide.END)); +}; + + +/** @override */ +goog.ui.ButtonRenderer.prototype.getCssClass = function() { + return goog.ui.ButtonRenderer.CSS_CLASS; +}; diff --git a/closure-library/closure/goog/ui/buttonside.js b/closure-library/closure/goog/ui/buttonside.js new file mode 100644 index 0000000000..405715971b --- /dev/null +++ b/closure-library/closure/goog/ui/buttonside.js @@ -0,0 +1,39 @@ +// Copyright 2010 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Enum for button side constants. In its own file so as to not + * cause a circular dependency with {@link goog.ui.ButtonRenderer}. + * + * @author doughtie@google.com (Gavin Doughtie) + */ + +goog.provide('goog.ui.ButtonSide'); + + +/** + * Constants for button sides, see {@link goog.ui.Button.prototype.setCollapsed} + * for details. + * @enum {number} + */ +goog.ui.ButtonSide = { + /** Neither side. */ + NONE: 0, + /** Left for LTR, right for RTL. */ + START: 1, + /** Right for LTR, left for RTL. */ + END: 2, + /** Both sides. */ + BOTH: 3 +}; diff --git a/closure-library/closure/goog/ui/charcounter.js b/closure-library/closure/goog/ui/charcounter.js new file mode 100644 index 0000000000..a6e72d7151 --- /dev/null +++ b/closure-library/closure/goog/ui/charcounter.js @@ -0,0 +1,198 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Character counter widget implementation. + * + * @author eae@google.com (Emil A Eklund) + * @see ../demos/charcounter.html + */ + +goog.provide('goog.ui.CharCounter'); +goog.provide('goog.ui.CharCounter.Display'); + +goog.require('goog.dom'); +goog.require('goog.events'); +goog.require('goog.events.EventTarget'); +goog.require('goog.events.InputHandler'); + + + +/** + * CharCounter widget. Counts the number of characters in a input field or a + * text box and displays the number of additional characters that may be + * entered before the maximum length is reached. + * + * @extends {goog.events.EventTarget} + * @param {HTMLInputElement|HTMLTextAreaElement} elInput Input or text area + * element to count the number of characters in. + * @param {Element} elCount HTML element to display the remaining number of + * characters in. You can pass in null for this if you don't want to expose + * the number of chars remaining. + * @param {number} maxLength The maximum length. + * @param {goog.ui.CharCounter.Display=} opt_displayMode Display mode for this + * char counter. Defaults to {@link goog.ui.CharCounter.Display.REMAINING}. + * @constructor + * @final + */ +goog.ui.CharCounter = function(elInput, elCount, maxLength, opt_displayMode) { + goog.events.EventTarget.call(this); + + /** + * Input or text area element to count the number of characters in. + * @type {HTMLInputElement|HTMLTextAreaElement} + * @private + */ + this.elInput_ = elInput; + + /** + * HTML element to display the remaining number of characters in. + * @type {Element} + * @private + */ + this.elCount_ = elCount; + + /** + * The maximum length. + * @type {number} + * @private + */ + this.maxLength_ = maxLength; + + /** + * The display mode for this char counter. + * @type {!goog.ui.CharCounter.Display} + * @private + */ + this.display_ = opt_displayMode || goog.ui.CharCounter.Display.REMAINING; + + elInput.removeAttribute('maxlength'); + + /** + * The input handler that provides the input event. + * @type {goog.events.InputHandler} + * @private + */ + this.inputHandler_ = new goog.events.InputHandler(elInput); + + goog.events.listen( + this.inputHandler_, goog.events.InputHandler.EventType.INPUT, + this.onChange_, false, this); + + this.checkLength(); +}; +goog.inherits(goog.ui.CharCounter, goog.events.EventTarget); + + +/** + * Display mode for the char counter. + * @enum {number} + */ +goog.ui.CharCounter.Display = { + /** Widget displays the number of characters remaining (the default). */ + REMAINING: 0, + /** Widget displays the number of characters entered. */ + INCREMENTAL: 1 +}; + + +/** + * Sets the maximum length. + * + * @param {number} maxLength The maximum length. + */ +goog.ui.CharCounter.prototype.setMaxLength = function(maxLength) { + this.maxLength_ = maxLength; + this.checkLength(); +}; + + +/** + * Returns the maximum length. + * + * @return {number} The maximum length. + */ +goog.ui.CharCounter.prototype.getMaxLength = function() { + return this.maxLength_; +}; + + +/** + * Sets the display mode. + * + * @param {!goog.ui.CharCounter.Display} displayMode The display mode. + */ +goog.ui.CharCounter.prototype.setDisplayMode = function(displayMode) { + this.display_ = displayMode; + this.checkLength(); +}; + + +/** + * Returns the display mode. + * + * @return {!goog.ui.CharCounter.Display} The display mode. + */ +goog.ui.CharCounter.prototype.getDisplayMode = function() { + return this.display_; +}; + + +/** + * Change event handler for input field. + * + * @param {goog.events.BrowserEvent} event Change event. + * @private + */ +goog.ui.CharCounter.prototype.onChange_ = function(event) { + this.checkLength(); +}; + + +/** + * Checks length of text in input field and updates the counter. Truncates text + * if the maximum lengths is exceeded. + */ +goog.ui.CharCounter.prototype.checkLength = function() { + var count = this.elInput_.value.length; + + // There's no maxlength property for textareas so instead we truncate the + // text if it gets too long. It's also used to truncate the text in a input + // field if the maximum length is changed. + if (count > this.maxLength_) { + var scrollTop = this.elInput_.scrollTop; + var scrollLeft = this.elInput_.scrollLeft; + + this.elInput_.value = this.elInput_.value.substring(0, this.maxLength_); + count = this.maxLength_; + + this.elInput_.scrollTop = scrollTop; + this.elInput_.scrollLeft = scrollLeft; + } + + if (this.elCount_) { + var incremental = this.display_ == goog.ui.CharCounter.Display.INCREMENTAL; + goog.dom.setTextContent( + this.elCount_, String(incremental ? count : this.maxLength_ - count)); + } +}; + + +/** @override */ +goog.ui.CharCounter.prototype.disposeInternal = function() { + goog.ui.CharCounter.superClass_.disposeInternal.call(this); + delete this.elInput_; + this.inputHandler_.dispose(); + this.inputHandler_ = null; +}; diff --git a/closure-library/closure/goog/ui/charpicker.js b/closure-library/closure/goog/ui/charpicker.js new file mode 100644 index 0000000000..de841f3e31 --- /dev/null +++ b/closure-library/closure/goog/ui/charpicker.js @@ -0,0 +1,924 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Character Picker widget for picking any Unicode character. + * + * @see ../demos/charpicker.html + */ + +goog.provide('goog.ui.CharPicker'); + +goog.require('goog.a11y.aria'); +goog.require('goog.a11y.aria.State'); +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.classlist'); +goog.require('goog.events'); +goog.require('goog.events.Event'); +goog.require('goog.events.EventHandler'); +goog.require('goog.events.EventType'); +goog.require('goog.events.InputHandler'); +goog.require('goog.events.KeyCodes'); +goog.require('goog.events.KeyHandler'); +goog.require('goog.i18n.CharListDecompressor'); +goog.require('goog.i18n.CharPickerData'); +goog.require('goog.i18n.uChar'); +goog.require('goog.i18n.uChar.NameFetcher'); +goog.require('goog.structs.Set'); +goog.require('goog.style'); +goog.require('goog.ui.Button'); +goog.require('goog.ui.Component'); +goog.require('goog.ui.ContainerScroller'); +goog.require('goog.ui.FlatButtonRenderer'); +goog.require('goog.ui.HoverCard'); +goog.require('goog.ui.LabelInput'); +goog.require('goog.ui.Menu'); +goog.require('goog.ui.MenuButton'); +goog.require('goog.ui.MenuItem'); +goog.require('goog.ui.Tooltip'); + + + +/** + * Character Picker Class. This widget can be used to pick any Unicode + * character by traversing a category-subcategory structure or by inputing its + * hex value. + * + * See charpicker.html demo for example usage. + * @param {goog.i18n.CharPickerData} charPickerData Category names and charlist. + * @param {!goog.i18n.uChar.NameFetcher} charNameFetcher Object which fetches + * the names of the characters that are shown in the widget. These names + * may be stored locally or come from an external source. + * @param {Array=} opt_recents List of characters to be displayed in + * resently selected characters area. + * @param {number=} opt_initCategory Sequence number of initial category. + * @param {number=} opt_initSubcategory Sequence number of initial subcategory. + * @param {number=} opt_rowCount Number of rows in the grid. + * @param {number=} opt_columnCount Number of columns in the grid. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper. + * @constructor + * @extends {goog.ui.Component} + * @final + */ +goog.ui.CharPicker = function( + charPickerData, charNameFetcher, opt_recents, opt_initCategory, + opt_initSubcategory, opt_rowCount, opt_columnCount, opt_domHelper) { + goog.ui.Component.call(this, opt_domHelper); + + /** + * Object used to retrieve character names. + * @type {!goog.i18n.uChar.NameFetcher} + * @private + */ + this.charNameFetcher_ = charNameFetcher; + + /** + * Object containing character lists and category names. + * @type {goog.i18n.CharPickerData} + * @private + */ + this.data_ = charPickerData; + + /** + * The category number to be used on widget init. + * @type {number} + * @private + */ + this.initCategory_ = opt_initCategory || 0; + + /** + * The subcategory number to be used on widget init. + * @type {number} + * @private + */ + this.initSubcategory_ = opt_initSubcategory || 0; + + /** + * Number of columns in the grid. + * @type {number} + * @private + */ + this.columnCount_ = opt_columnCount || 10; + + /** + * Number of entries to be added to the grid. + * @type {number} + * @private + */ + this.gridsize_ = (opt_rowCount || 10) * this.columnCount_; + + /** + * Number of the recently selected characters displayed. + * @type {number} + * @private + */ + this.recentwidth_ = this.columnCount_ + 1; + + /** + * List of recently used characters. + * @type {Array} + * @private + */ + this.recents_ = opt_recents || []; + + /** + * Handler for events. + * @type {goog.events.EventHandler} + * @private + */ + this.eventHandler_ = new goog.events.EventHandler(this); + + /** + * Decompressor used to get the list of characters from a base88 encoded + * character list. + * @type {Object} + * @private + */ + this.decompressor_ = new goog.i18n.CharListDecompressor(); +}; +goog.inherits(goog.ui.CharPicker, goog.ui.Component); + + +/** + * The last selected character. + * @type {?string} + * @private + */ +goog.ui.CharPicker.prototype.selectedChar_ = null; + + +/** + * Set of formatting characters whose display need to be swapped with nbsp + * to prevent layout issues. + * @type {goog.structs.Set} + * @private + */ +goog.ui.CharPicker.prototype.layoutAlteringChars_ = null; + + +/** + * The top category menu. + * @type {goog.ui.Menu} + * @private + */ +goog.ui.CharPicker.prototype.menu_ = null; + + +/** + * The top category menu button. + * @type {goog.ui.MenuButton} + * @private + */ +goog.ui.CharPicker.prototype.menubutton_ = null; + + +/** + * The subcategory menu. + * @type {goog.ui.Menu} + * @private + */ +goog.ui.CharPicker.prototype.submenu_ = null; + + +/** + * The subcategory menu button. + * @type {goog.ui.MenuButton} + * @private + */ +goog.ui.CharPicker.prototype.submenubutton_ = null; + + +/** @type {number} */ +goog.ui.CharPicker.prototype.itempos; + + +/** @type {!Array} */ +goog.ui.CharPicker.prototype.items; + + +/** @private {!goog.events.KeyHandler} */ +goog.ui.CharPicker.prototype.keyHandler_; + + +/** + * Category index used to index the data tables. + * @type {number} + */ +goog.ui.CharPicker.prototype.category; + + +/** @private {Element} */ +goog.ui.CharPicker.prototype.stick_ = null; + + +/** + * The element representing the number of rows visible in the grid. + * This along with goog.ui.CharPicker.stick_ would help to create a scrollbar + * of right size. + * @type {HTMLElement} + * @private + */ +goog.ui.CharPicker.prototype.stickwrap_ = null; + + +/** + * The component containing all the buttons for each character in display. + * @type {goog.ui.Component} + * @private + */ +goog.ui.CharPicker.prototype.grid_ = null; + + +/** + * The component used for extra information about the character set displayed. + * @type {goog.ui.Component} + * @private + */ +goog.ui.CharPicker.prototype.notice_ = null; + + +/** + * Grid displaying recently selected characters. + * @type {goog.ui.Component} + * @private + */ +goog.ui.CharPicker.prototype.recentgrid_ = null; + + +/** + * Input field for entering the hex value of the character. + * @type {goog.ui.Component} + * @private + */ +goog.ui.CharPicker.prototype.input_ = null; + + +/** + * OK button for entering hex value of the character. + * @private {goog.ui.Button} + */ +goog.ui.CharPicker.prototype.okbutton_ = null; + + +/** + * Element displaying character name in preview. + * @type {Element} + * @private + */ +goog.ui.CharPicker.prototype.charNameEl_ = null; + + +/** + * Element displaying character in preview. + * @type {Element} + * @private + */ +goog.ui.CharPicker.prototype.zoomEl_ = null; + + +/** + * Element displaying character number (codepoint) in preview. + * @type {Element} + * @private + */ +goog.ui.CharPicker.prototype.unicodeEl_ = null; + + +/** + * Hover card for displaying the preview of a character. + * Preview would contain character in large size and its U+ notation. It would + * also display the name, if available. + * @type {goog.ui.HoverCard} + * @private + */ +goog.ui.CharPicker.prototype.hc_ = null; + + +/** + * Gets the last selected character. + * @return {?string} The last selected character. + */ +goog.ui.CharPicker.prototype.getSelectedChar = function() { + return this.selectedChar_; +}; + + +/** + * Gets the list of characters user selected recently. + * @return {Array} The recent character list. + */ +goog.ui.CharPicker.prototype.getRecentChars = function() { + return this.recents_; +}; + + +/** @override */ +goog.ui.CharPicker.prototype.createDom = function() { + goog.ui.CharPicker.superClass_.createDom.call(this); + + this.decorateInternal( + this.getDomHelper().createElement(goog.dom.TagName.DIV)); +}; + + +/** @override */ +goog.ui.CharPicker.prototype.disposeInternal = function() { + goog.dispose(this.hc_); + this.hc_ = null; + goog.dispose(this.eventHandler_); + this.eventHandler_ = null; + goog.ui.CharPicker.superClass_.disposeInternal.call(this); +}; + + +/** @override */ +goog.ui.CharPicker.prototype.decorateInternal = function(element) { + goog.ui.CharPicker.superClass_.decorateInternal.call(this, element); + + // The chars below cause layout disruption or too narrow to hover: + // \u0020, \u00AD, \u2000 - \u200f, \u2028 - \u202f, \u3000, \ufeff + var chrs = this.decompressor_.toCharList(':2%C^O80V1H2s2G40Q%s0'); + this.layoutAlteringChars_ = new goog.structs.Set(chrs); + + this.menu_ = new goog.ui.Menu(this.getDomHelper()); + + var categories = this.data_.categories; + for (var i = 0; i < this.data_.categories.length; i++) { + this.menu_.addChild(this.createMenuItem_(i, categories[i]), true); + } + + this.menubutton_ = new goog.ui.MenuButton( + 'Category Menu', this.menu_, + /* opt_renderer */ undefined, this.getDomHelper()); + this.addChild(this.menubutton_, true); + + this.submenu_ = new goog.ui.Menu(this.getDomHelper()); + + this.submenubutton_ = new goog.ui.MenuButton( + 'Subcategory Menu', this.submenu_, /* opt_renderer */ undefined, + this.getDomHelper()); + this.addChild(this.submenubutton_, true); + + // The containing component for grid component and the scroller. + var gridcontainer = new goog.ui.Component(this.getDomHelper()); + this.addChild(gridcontainer, true); + + var stickwrap = new goog.ui.Component(this.getDomHelper()); + gridcontainer.addChild(stickwrap, true); + this.stickwrap_ = /** @type {!HTMLElement} */ (stickwrap.getElement()); + + var stick = new goog.ui.Component(this.getDomHelper()); + stickwrap.addChild(stick, true); + this.stick_ = stick.getElement(); + + this.grid_ = new goog.ui.Component(this.getDomHelper()); + gridcontainer.addChild(this.grid_, true); + + this.notice_ = new goog.ui.Component(this.getDomHelper()); + this.notice_.setElementInternal( + this.getDomHelper().createDom(goog.dom.TagName.DIV)); + this.addChild(this.notice_, true); + + // The component used for displaying 'Recent Selections' label. + /** + * @desc The text label above the list of recently selected characters. + */ + var MSG_CHAR_PICKER_RECENT_SELECTIONS = goog.getMsg('Recent Selections:'); + var recenttext = new goog.ui.Component(this.getDomHelper()); + recenttext.setElementInternal( + this.getDomHelper().createDom( + goog.dom.TagName.SPAN, null, MSG_CHAR_PICKER_RECENT_SELECTIONS)); + this.addChild(recenttext, true); + + this.recentgrid_ = new goog.ui.Component(this.getDomHelper()); + this.addChild(this.recentgrid_, true); + + // The component used for displaying 'U+'. + var uplus = new goog.ui.Component(this.getDomHelper()); + uplus.setElementInternal( + this.getDomHelper().createDom(goog.dom.TagName.SPAN, null, 'U+')); + this.addChild(uplus, true); + + /** + * @desc The text inside the input box to specify the hex code of a character. + */ + var MSG_CHAR_PICKER_HEX_INPUT = goog.getMsg('Hex Input'); + this.input_ = + new goog.ui.LabelInput(MSG_CHAR_PICKER_HEX_INPUT, this.getDomHelper()); + this.addChild(this.input_, true); + + this.okbutton_ = new goog.ui.Button( + 'OK', /* opt_renderer */ undefined, this.getDomHelper()); + this.addChild(this.okbutton_, true); + this.okbutton_.setEnabled(false); + + this.zoomEl_ = this.getDomHelper().createDom( + goog.dom.TagName.DIV, + {id: 'zoom', className: goog.getCssName('goog-char-picker-char-zoom')}); + + this.charNameEl_ = this.getDomHelper().createDom( + goog.dom.TagName.DIV, + {id: 'charName', className: goog.getCssName('goog-char-picker-name')}); + + this.unicodeEl_ = this.getDomHelper().createDom( + goog.dom.TagName.DIV, + {id: 'unicode', className: goog.getCssName('goog-char-picker-unicode')}); + + var card = this.getDomHelper().createDom( + goog.dom.TagName.DIV, {'id': 'preview'}, this.zoomEl_, this.charNameEl_, + this.unicodeEl_); + goog.style.setElementShown(card, false); + this.hc_ = new goog.ui.HoverCard( + {'DIV': 'char'}, + /* opt_checkDescendants */ undefined, this.getDomHelper()); + this.hc_.setElement(card); + var self = this; + + /** + * Function called by hover card just before it is visible to collect data. + */ + function onBeforeShow() { + var trigger = self.hc_.getAnchorElement(); + var ch = self.getChar_(trigger); + if (ch) { + goog.dom.setTextContent(self.zoomEl_, self.displayChar_(ch)); + goog.dom.setTextContent(self.unicodeEl_, goog.i18n.uChar.toHexString(ch)); + // Clear the character name since we don't want to show old data because + // it is retrieved asynchronously and the DOM object is re-used + goog.dom.setTextContent(self.charNameEl_, ''); + self.charNameFetcher_.getName(ch, function(charName) { + if (charName) { + goog.dom.setTextContent(self.charNameEl_, charName); + } + }); + } + } + + goog.events.listen( + this.hc_, goog.ui.HoverCard.EventType.BEFORE_SHOW, onBeforeShow); + goog.asserts.assert(element); + goog.dom.classlist.add(element, goog.getCssName('goog-char-picker')); + goog.dom.classlist.add( + goog.asserts.assert(this.stick_), goog.getCssName('goog-stick')); + goog.dom.classlist.add( + goog.asserts.assert(this.stickwrap_), goog.getCssName('goog-stickwrap')); + goog.dom.classlist.add( + goog.asserts.assert(gridcontainer.getElement()), + goog.getCssName('goog-char-picker-grid-container')); + goog.dom.classlist.add( + goog.asserts.assert(this.grid_.getElement()), + goog.getCssName('goog-char-picker-grid')); + goog.dom.classlist.add( + goog.asserts.assert(this.recentgrid_.getElement()), + goog.getCssName('goog-char-picker-grid')); + goog.dom.classlist.add( + goog.asserts.assert(this.recentgrid_.getElement()), + goog.getCssName('goog-char-picker-recents')); + + goog.dom.classlist.add( + goog.asserts.assert(this.notice_.getElement()), + goog.getCssName('goog-char-picker-notice')); + goog.dom.classlist.add( + goog.asserts.assert(uplus.getElement()), + goog.getCssName('goog-char-picker-uplus')); + goog.dom.classlist.add( + goog.asserts.assert(this.input_.getElement()), + goog.getCssName('goog-char-picker-input-box')); + goog.dom.classlist.add( + goog.asserts.assert(this.okbutton_.getElement()), + goog.getCssName('goog-char-picker-okbutton')); + goog.dom.classlist.add( + goog.asserts.assert(card), goog.getCssName('goog-char-picker-hovercard')); + + this.hc_.className = goog.getCssName('goog-char-picker-hovercard'); + + this.grid_.buttoncount = this.gridsize_; + this.recentgrid_.buttoncount = this.recentwidth_; + this.populateGridWithButtons_(this.grid_); + this.populateGridWithButtons_(this.recentgrid_); + + this.updateGrid_(this.recentgrid_, this.recents_); + this.setSelectedCategory_(this.initCategory_, this.initSubcategory_); + new goog.ui.ContainerScroller(this.menu_); + new goog.ui.ContainerScroller(this.submenu_); + + goog.dom.classlist.add( + goog.asserts.assert(this.menu_.getElement()), + goog.getCssName('goog-char-picker-menu')); + goog.dom.classlist.add( + goog.asserts.assert(this.submenu_.getElement()), + goog.getCssName('goog-char-picker-menu')); +}; + + +/** @override */ +goog.ui.CharPicker.prototype.enterDocument = function() { + goog.ui.CharPicker.superClass_.enterDocument.call(this); + var inputkh = new goog.events.InputHandler(this.input_.getElement()); + this.keyHandler_ = new goog.events.KeyHandler(this.input_.getElement()); + + // Stop the propagation of ACTION events at menu and submenu buttons. + // If stopped at capture phase, the button will not be set to normal state. + // If not stopped, the user widget will receive the event, which is + // undesired. User widget should receive an event only on the character + // click. + this.eventHandler_ + .listen( + this.menubutton_, goog.ui.Component.EventType.ACTION, + goog.events.Event.stopPropagation) + .listen( + this.submenubutton_, goog.ui.Component.EventType.ACTION, + goog.events.Event.stopPropagation) + .listen( + this, goog.ui.Component.EventType.ACTION, this.handleSelectedItem_, + true) + .listen( + inputkh, goog.events.InputHandler.EventType.INPUT, this.handleInput_) + .listen( + this.keyHandler_, goog.events.KeyHandler.EventType.KEY, + this.handleEnter_) + .listen( + this.recentgrid_, goog.ui.Component.EventType.FOCUS, + this.handleFocus_) + .listen(this.grid_, goog.ui.Component.EventType.FOCUS, this.handleFocus_); + + goog.events.listen( + this.okbutton_.getElement(), goog.events.EventType.MOUSEDOWN, + this.handleOkClick_, true, this); + + goog.events.listen( + this.stickwrap_, goog.events.EventType.SCROLL, this.handleScroll_, true, + this); +}; + + +/** + * Handles the button focus by updating the aria label with the character name + * so it becomes possible to get spoken feedback while tabbing through the + * visible symbols. + * @param {goog.events.Event} e The focus event. + * @private + */ +goog.ui.CharPicker.prototype.handleFocus_ = function(e) { + var button = e.target; + var element = /** @type {!Element} */ (button.getElement()); + var ch = this.getChar_(element); + + // Clear the aria label to avoid speaking the old value in case the button + // element has no char attribute or the character name cannot be retrieved. + goog.a11y.aria.setState(element, goog.a11y.aria.State.LABEL, ''); + + if (ch) { + // This is working with screen readers because the call to getName is + // synchronous once the values have been prefetched by the RemoteNameFetcher + // and because it is always synchronous when using the LocalNameFetcher. + // Also, the special character itself is not used as the label because some + // screen readers, notably ChromeVox, are not able to speak them. + // TODO(user): Consider changing the NameFetcher API to provide a + // method that lets the caller retrieve multiple character names at once + // so that this asynchronous gymnastic can be avoided. + this.charNameFetcher_.getName(ch, function(charName) { + if (charName) { + goog.a11y.aria.setState(element, goog.a11y.aria.State.LABEL, charName); + } + }); + } +}; + + +/** + * On scroll, updates the grid with characters correct to the scroll position. + * @param {goog.events.Event} e Scroll event to handle. + * @private + */ +goog.ui.CharPicker.prototype.handleScroll_ = function(e) { + var height = e.target.scrollHeight; + var top = e.target.scrollTop; + var itempos = + Math.ceil(top * this.items.length / (this.columnCount_ * height)) * + this.columnCount_; + if (this.itempos != itempos) { + this.itempos = itempos; + this.modifyGridWithItems_(this.grid_, this.items, itempos); + } + e.stopPropagation(); +}; + + +/** + * On a menu click, sets correct character set in the grid; on a grid click + * accept the character as the selected one and adds to recent selection, if not + * already present. + * @param {goog.events.Event} e Event for the click on menus or grid. + * @private + */ +goog.ui.CharPicker.prototype.handleSelectedItem_ = function(e) { + var parent = /** @type {goog.ui.Component} */ (e.target).getParent(); + if (parent == this.menu_) { + this.menu_.setVisible(false); + this.setSelectedCategory_(e.target.getValue()); + } else if (parent == this.submenu_) { + this.submenu_.setVisible(false); + this.setSelectedSubcategory_(e.target.getValue()); + } else if (parent == this.grid_) { + var button = e.target.getElement(); + this.selectedChar_ = this.getChar_(button); + this.updateRecents_(this.selectedChar_); + } else if (parent == this.recentgrid_) { + this.selectedChar_ = this.getChar_(e.target.getElement()); + } +}; + + +/** + * When user types the characters displays the preview. Enables the OK button, + * if the character is valid. + * @param {goog.events.Event} e Event for typing in input field. + * @private + */ +goog.ui.CharPicker.prototype.handleInput_ = function(e) { + var ch = this.getInputChar(); + if (ch) { + goog.dom.setTextContent(this.zoomEl_, ch); + goog.dom.setTextContent(this.unicodeEl_, goog.i18n.uChar.toHexString(ch)); + goog.dom.setTextContent(this.charNameEl_, ''); + var coord = + new goog.ui.Tooltip.ElementTooltipPosition(this.input_.getElement()); + this.hc_.setPosition(coord); + this.hc_.triggerForElement(this.input_.getElement()); + this.okbutton_.setEnabled(true); + } else { + this.hc_.cancelTrigger(); + this.hc_.setVisible(false); + this.okbutton_.setEnabled(false); + } +}; + + +/** + * On OK click accepts the character and updates the recent char list. + * @param {goog.events.Event=} opt_event Event for click on OK button. + * @return {boolean} Indicates whether to propagate event. + * @private + */ +goog.ui.CharPicker.prototype.handleOkClick_ = function(opt_event) { + var ch = this.getInputChar(); + if (ch && ch.charCodeAt(0)) { + this.selectedChar_ = ch; + this.updateRecents_(ch); + return true; + } + return false; +}; + + +/** + * Behaves exactly like the OK button on Enter key. + * @param {goog.events.KeyEvent} e Event for enter on the input field. + * @return {boolean} Indicates whether to propagate event. + * @private + */ +goog.ui.CharPicker.prototype.handleEnter_ = function(e) { + if (e.keyCode == goog.events.KeyCodes.ENTER) { + return this.handleOkClick_() ? + this.dispatchEvent(goog.ui.Component.EventType.ACTION) : + false; + } + return false; +}; + + +/** + * Gets the character from the event target. + * @param {Element} e Event target containing the 'char' attribute. + * @return {string} The character specified in the event. + * @private + */ +goog.ui.CharPicker.prototype.getChar_ = function(e) { + return e.getAttribute('char'); +}; + + +/** + * Creates a menu entry for either the category listing or subcategory listing. + * @param {number} id Id to be used for the entry. + * @param {string} caption Text displayed for the menu item. + * @return {!goog.ui.MenuItem} Menu item to be added to the menu listing. + * @private + */ +goog.ui.CharPicker.prototype.createMenuItem_ = function(id, caption) { + var item = new goog.ui.MenuItem(caption, /* model */ id, this.getDomHelper()); + item.setVisible(true); + return item; +}; + + +/** + * Sets the category and updates the submenu items and grid accordingly. + * @param {number} category Category index used to index the data tables. + * @param {number=} opt_subcategory Subcategory index used with category index. + * @private + */ +goog.ui.CharPicker.prototype.setSelectedCategory_ = function( + category, opt_subcategory) { + this.category = category; + this.menubutton_.setCaption(this.data_.categories[category]); + while (this.submenu_.hasChildren()) { + this.submenu_.removeChildAt(0, true).dispose(); + } + + var subcategories = this.data_.subcategories[category]; + for (var i = 0; i < subcategories.length; i++) { + var item = this.createMenuItem_(i, subcategories[i]); + this.submenu_.addChild(item, true); + } + this.setSelectedSubcategory_(opt_subcategory || 0); +}; + + +/** + * Sets the subcategory and updates the grid accordingly. + * @param {number} subcategory Sub-category index used to index the data tables. + * @private + */ +goog.ui.CharPicker.prototype.setSelectedSubcategory_ = function(subcategory) { + var subcategories = this.data_.subcategories; + var name = subcategories[this.category][subcategory]; + this.submenubutton_.setCaption(name); + this.setSelectedGrid_(this.category, subcategory); +}; + + +/** + * Updates the grid according to a given category and subcategory. + * @param {number} category Index to the category table. + * @param {number} subcategory Index to the subcategory table. + * @private + */ +goog.ui.CharPicker.prototype.setSelectedGrid_ = function( + category, subcategory) { + var charLists = this.data_.charList; + var charListStr = charLists[category][subcategory]; + var content = this.decompressor_.toCharList(charListStr); + this.charNameFetcher_.prefetch(charListStr); + this.updateGrid_(this.grid_, content); +}; + + +/** + * Updates the grid with new character list. + * @param {goog.ui.Component} grid The grid which is updated with a new set of + * characters. + * @param {Array} items Characters to be added to the grid. + * @private + */ +goog.ui.CharPicker.prototype.updateGrid_ = function(grid, items) { + if (grid == this.grid_) { + /** + * @desc The message used when there are invisible characters like space + * or format control characters. + */ + var MSG_PLEASE_HOVER = + goog.getMsg('Please hover over each cell for the character name.'); + + goog.dom.setTextContent( + this.notice_.getElement(), + this.charNameFetcher_.isNameAvailable(items[0]) ? MSG_PLEASE_HOVER : + ''); + this.items = items; + if (this.stickwrap_.offsetHeight > 0) { + this.stick_.style.height = + this.stickwrap_.offsetHeight * items.length / this.gridsize_ + 'px'; + } else { + // This is the last ditch effort if height is not avaialble. + // Maximum of 3em is assumed to the the cell height. Extra space after + // last character in the grid is OK. + this.stick_.style.height = + 3 * this.columnCount_ * items.length / this.gridsize_ + 'em'; + } + this.stickwrap_.scrollTop = 0; + } + + this.modifyGridWithItems_(grid, items, 0); +}; + + +/** + * Updates the grid with new character list for a given starting point. + * @param {goog.ui.Component} grid The grid which is updated with a new set of + * characters. + * @param {Array} items Characters to be added to the grid. + * @param {number} start The index from which the characters should be + * displayed. + * @private + */ +goog.ui.CharPicker.prototype.modifyGridWithItems_ = function( + grid, items, start) { + for (var buttonpos = 0, itempos = start; + buttonpos < grid.buttoncount && itempos < items.length; + buttonpos++, itempos++) { + this.modifyCharNode_( + /** @type {!goog.ui.Button} */ (grid.getChildAt(buttonpos)), + items[itempos]); + } + + for (; buttonpos < grid.buttoncount; buttonpos++) { + grid.getChildAt(buttonpos).setVisible(false); + } +}; + + +/** + * Creates the grid for characters to displayed for selection. + * @param {goog.ui.Component} grid The grid which is updated with a new set of + * characters. + * @private + */ +goog.ui.CharPicker.prototype.populateGridWithButtons_ = function(grid) { + for (var i = 0; i < grid.buttoncount; i++) { + var button = new goog.ui.Button( + ' ', goog.ui.FlatButtonRenderer.getInstance(), this.getDomHelper()); + + // Dispatch the focus event so we can update the aria description while + // the user tabs through the cells. + button.setDispatchTransitionEvents(goog.ui.Component.State.FOCUSED, true); + + grid.addChild(button, true); + button.setVisible(false); + + var buttonEl = button.getElement(); + goog.asserts.assert(buttonEl, 'The button DOM element cannot be null.'); + + // Override the button role so the user doesn't hear "button" each time he + // tabs through the cells. + goog.a11y.aria.removeRole(buttonEl); + } +}; + + +/** + * Updates the grid cell with new character. + * @param {goog.ui.Button} button This button is popped up for new character. + * @param {string} ch Character to be displayed by the button. + * @private + */ +goog.ui.CharPicker.prototype.modifyCharNode_ = function(button, ch) { + var text = this.displayChar_(ch); + var buttonEl = button.getElement(); + goog.dom.setTextContent(buttonEl, text); + buttonEl.setAttribute('char', ch); + button.setVisible(true); +}; + + +/** + * Adds a given character to the recent character list. + * @param {string} character Character to be added to the recent list. + * @private + */ +goog.ui.CharPicker.prototype.updateRecents_ = function(character) { + if (character && character.charCodeAt(0) && + !goog.array.contains(this.recents_, character)) { + this.recents_.unshift(character); + if (this.recents_.length > this.recentwidth_) { + this.recents_.pop(); + } + this.updateGrid_(this.recentgrid_, this.recents_); + } +}; + + +/** + * Gets the user inputed unicode character. + * @return {string} Unicode character inputed by user. + */ +goog.ui.CharPicker.prototype.getInputChar = function() { + var text = this.input_.getValue(); + var code = parseInt(text, 16); + return /** @type {string} */ (goog.i18n.uChar.fromCharCode(code)); +}; + + +/** + * Gets the display character for the given character. + * @param {string} ch Character whose display is fetched. + * @return {string} The display of the given character. + * @private + */ +goog.ui.CharPicker.prototype.displayChar_ = function(ch) { + return this.layoutAlteringChars_.contains(ch) ? '\u00A0' : ch; +}; diff --git a/closure-library/closure/goog/ui/checkbox.js b/closure-library/closure/goog/ui/checkbox.js new file mode 100644 index 0000000000..7ae5c951e7 --- /dev/null +++ b/closure-library/closure/goog/ui/checkbox.js @@ -0,0 +1,263 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Tristate checkbox widget. + * + * @see ../demos/checkbox.html + */ + +goog.provide('goog.ui.Checkbox'); +goog.provide('goog.ui.Checkbox.State'); + +goog.require('goog.a11y.aria'); +goog.require('goog.a11y.aria.State'); +goog.require('goog.events.EventType'); +goog.require('goog.events.KeyCodes'); +goog.require('goog.string'); +goog.require('goog.ui.CheckboxRenderer'); +goog.require('goog.ui.Component'); +goog.require('goog.ui.Control'); +goog.require('goog.ui.registry'); + + + +/** + * 3-state checkbox widget. Fires CHECK or UNCHECK events before toggled and + * CHANGE event after toggled by user. + * The checkbox can also be enabled/disabled and get focused and highlighted. + * + * @param {goog.ui.Checkbox.State=} opt_checked Checked state to set. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for + * document interaction. + * @param {goog.ui.CheckboxRenderer=} opt_renderer Renderer used to render or + * decorate the checkbox; defaults to {@link goog.ui.CheckboxRenderer}. + * @constructor + * @extends {goog.ui.Control} + */ +goog.ui.Checkbox = function(opt_checked, opt_domHelper, opt_renderer) { + var renderer = opt_renderer || goog.ui.CheckboxRenderer.getInstance(); + goog.ui.Control.call(this, null, renderer, opt_domHelper); + // The checkbox maintains its own tri-state CHECKED state. + // The control class maintains DISABLED, ACTIVE, and FOCUSED (which enable tab + // navigation, and keyHandling with SPACE). + + /** + * Checked state of the checkbox. + * @type {goog.ui.Checkbox.State} + * @private + */ + this.checked_ = + goog.isDef(opt_checked) ? opt_checked : goog.ui.Checkbox.State.UNCHECKED; +}; +goog.inherits(goog.ui.Checkbox, goog.ui.Control); +goog.tagUnsealableClass(goog.ui.Checkbox); + + +/** + * Possible checkbox states. + * @enum {?boolean} + */ +goog.ui.Checkbox.State = { + CHECKED: true, + UNCHECKED: false, + UNDETERMINED: null +}; + + +/** + * Label element bound to the checkbox. + * @type {Element} + * @private + */ +goog.ui.Checkbox.prototype.label_ = null; + + +/** + * @return {goog.ui.Checkbox.State} Checked state of the checkbox. + */ +goog.ui.Checkbox.prototype.getChecked = function() { + return this.checked_; +}; + + +/** + * @return {boolean} Whether the checkbox is checked. + * @override + */ +goog.ui.Checkbox.prototype.isChecked = function() { + return this.checked_ == goog.ui.Checkbox.State.CHECKED; +}; + + +/** + * @return {boolean} Whether the checkbox is not checked. + */ +goog.ui.Checkbox.prototype.isUnchecked = function() { + return this.checked_ == goog.ui.Checkbox.State.UNCHECKED; +}; + + +/** + * @return {boolean} Whether the checkbox is in partially checked state. + */ +goog.ui.Checkbox.prototype.isUndetermined = function() { + return this.checked_ == goog.ui.Checkbox.State.UNDETERMINED; +}; + + +/** + * Sets the checked state of the checkbox. + * @param {?boolean} checked The checked state to set. + * @override + */ +goog.ui.Checkbox.prototype.setChecked = function(checked) { + if (checked != this.checked_) { + this.checked_ = /** @type {goog.ui.Checkbox.State} */ (checked); + this.getRenderer().setCheckboxState(this.getElement(), this.checked_); + } +}; + + +/** + * Sets the checked state for the checkbox. Unlike {@link #setChecked}, + * doesn't update the checkbox's DOM. Considered protected; to be called + * only by renderer code during element decoration. + * @param {goog.ui.Checkbox.State} checked New checkbox state. + */ +goog.ui.Checkbox.prototype.setCheckedInternal = function(checked) { + this.checked_ = checked; +}; + + +/** + * Binds an HTML element to the checkbox which if clicked toggles the checkbox. + * Behaves the same way as the 'label' HTML tag. The label element has to be the + * direct or non-direct ancestor of the checkbox element because it will get the + * focus when keyboard support is implemented. + * Note: Control#enterDocument also sets aria-label on the element but + * Checkbox#enterDocument sets aria-labeledby on the same element which + * overrides the aria-label in all modern screen readers. + * + * @param {?Element} label The label control to set. If null, only the checkbox + * reacts to clicks. + */ +goog.ui.Checkbox.prototype.setLabel = function(label) { + if (this.isInDocument()) { + var wasFocused = this.isFocused(); + this.exitDocument(); + this.label_ = label; + this.enterDocument(); + if (wasFocused) { + this.getElementStrict().focus(); + } + } else { + this.label_ = label; + } +}; + + +/** + * Toggles the checkbox. State transitions: + *
        + *
      • unchecked -> checked + *
      • undetermined -> checked + *
      • checked -> unchecked + *
      + */ +goog.ui.Checkbox.prototype.toggle = function() { + this.setChecked( + this.checked_ ? goog.ui.Checkbox.State.UNCHECKED : + goog.ui.Checkbox.State.CHECKED); +}; + + +/** @override */ +goog.ui.Checkbox.prototype.enterDocument = function() { + goog.ui.Checkbox.base(this, 'enterDocument'); + if (this.isHandleMouseEvents()) { + var handler = this.getHandler(); + // Listen to the label, if it was set. + if (this.label_) { + // Any mouse events that happen to the associated label should have the + // same effect on the checkbox as if they were happening to the checkbox + // itself. + handler + .listen( + this.label_, goog.events.EventType.CLICK, + this.handleClickOrSpace_) + .listen( + this.label_, goog.events.EventType.MOUSEOVER, + this.handleMouseOver) + .listen( + this.label_, goog.events.EventType.MOUSEOUT, this.handleMouseOut) + .listen( + this.label_, goog.events.EventType.MOUSEDOWN, + this.handleMouseDown) + .listen( + this.label_, goog.events.EventType.MOUSEUP, this.handleMouseUp); + } + // Checkbox needs to explicitly listen for click event. + handler.listen( + this.getElement(), goog.events.EventType.CLICK, + this.handleClickOrSpace_); + } + + // Set aria label. + var checkboxElement = this.getElementStrict(); + if (this.label_ && checkboxElement != this.label_ && + goog.string.isEmptyOrWhitespace( + goog.a11y.aria.getLabel(checkboxElement))) { + if (!this.label_.id) { + this.label_.id = this.makeId('lbl'); + } + goog.a11y.aria.setState( + checkboxElement, goog.a11y.aria.State.LABELLEDBY, this.label_.id); + } +}; + + +/** + * Handles the click event. + * @param {!goog.events.BrowserEvent} e The event. + * @private + */ +goog.ui.Checkbox.prototype.handleClickOrSpace_ = function(e) { + e.stopPropagation(); + var eventType = this.checked_ ? goog.ui.Component.EventType.UNCHECK : + goog.ui.Component.EventType.CHECK; + if (this.isEnabled() && !e.target.href && this.dispatchEvent(eventType)) { + e.preventDefault(); // Prevent scrolling in Chrome if SPACE is pressed. + this.toggle(); + this.dispatchEvent(goog.ui.Component.EventType.CHANGE); + } +}; + + +/** @override */ +goog.ui.Checkbox.prototype.handleKeyEventInternal = function(e) { + if (e.keyCode == goog.events.KeyCodes.SPACE) { + this.performActionInternal(e); + this.handleClickOrSpace_(e); + } + return false; +}; + + +/** + * Register this control so it can be created from markup. + */ +goog.ui.registry.setDecoratorByClassName( + goog.ui.CheckboxRenderer.CSS_CLASS, + function() { return new goog.ui.Checkbox(); }); diff --git a/closure-library/closure/goog/ui/checkboxmenuitem.js b/closure-library/closure/goog/ui/checkboxmenuitem.js new file mode 100644 index 0000000000..29127016be --- /dev/null +++ b/closure-library/closure/goog/ui/checkboxmenuitem.js @@ -0,0 +1,53 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A menu item class that supports checkbox semantics. + * + * @author attila@google.com (Attila Bodis) + */ + +goog.provide('goog.ui.CheckBoxMenuItem'); + +goog.require('goog.ui.MenuItem'); +goog.require('goog.ui.registry'); + + + +/** + * Class representing a checkbox menu item. This is just a convenience class + * that extends {@link goog.ui.MenuItem} by making it checkable. + * + * @param {goog.ui.ControlContent} content Text caption or DOM structure to + * display as the content of the item (use to add icons or styling to + * menus). + * @param {*=} opt_model Data/model associated with the menu item. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper used for + * document interactions. + * @constructor + * @extends {goog.ui.MenuItem} + */ +goog.ui.CheckBoxMenuItem = function(content, opt_model, opt_domHelper) { + goog.ui.MenuItem.call(this, content, opt_model, opt_domHelper); + this.setCheckable(true); +}; +goog.inherits(goog.ui.CheckBoxMenuItem, goog.ui.MenuItem); + + +// Register a decorator factory function for goog.ui.CheckBoxMenuItems. +goog.ui.registry.setDecoratorByClassName( + goog.getCssName('goog-checkbox-menuitem'), function() { + // CheckBoxMenuItem defaults to using MenuItemRenderer. + return new goog.ui.CheckBoxMenuItem(null); + }); diff --git a/closure-library/closure/goog/ui/checkboxrenderer.js b/closure-library/closure/goog/ui/checkboxrenderer.js new file mode 100644 index 0000000000..35668cf379 --- /dev/null +++ b/closure-library/closure/goog/ui/checkboxrenderer.js @@ -0,0 +1,196 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// 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 Default renderer for {@link goog.ui.Checkbox}s. + * + */ + +goog.provide('goog.ui.CheckboxRenderer'); + +goog.require('goog.a11y.aria'); +goog.require('goog.a11y.aria.Role'); +goog.require('goog.a11y.aria.State'); +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.classlist'); +goog.require('goog.object'); +goog.require('goog.ui.ControlRenderer'); + +goog.forwardDeclare('goog.ui.Checkbox.State'); + + + +/** + * Default renderer for {@link goog.ui.Checkbox}s. Extends the superclass + * to support checkbox states: + * @constructor + * @extends {goog.ui.ControlRenderer} + */ +goog.ui.CheckboxRenderer = function() { + goog.ui.CheckboxRenderer.base(this, 'constructor'); +}; +goog.inherits(goog.ui.CheckboxRenderer, goog.ui.ControlRenderer); +goog.addSingletonGetter(goog.ui.CheckboxRenderer); + + +/** + * Default CSS class to be applied to the root element of components rendered + * by this renderer. + * @type {string} + */ +goog.ui.CheckboxRenderer.CSS_CLASS = goog.getCssName('goog-checkbox'); + + +/** @override */ +goog.ui.CheckboxRenderer.prototype.createDom = function(checkbox) { + var element = checkbox.getDomHelper().createDom( + goog.dom.TagName.SPAN, this.getClassNames(checkbox).join(' ')); + + var state = checkbox.getChecked(); + this.setCheckboxState(element, state); + + return element; +}; + + +/** @override */ +goog.ui.CheckboxRenderer.prototype.decorate = function(checkbox, element) { + // The superclass implementation takes care of common attributes; we only + // need to set the checkbox state. + element = goog.ui.CheckboxRenderer.base(this, 'decorate', checkbox, element); + goog.asserts.assert(element); + var classes = goog.dom.classlist.get(element); + // Update the checked state of the element based on its css classNames + // with the following order: undetermined -> checked -> unchecked. + var checked = + /** @suppress {missingRequire} */ (goog.ui.Checkbox.State.UNCHECKED); + if (goog.array.contains( + classes, this.getClassForCheckboxState( + /** @suppress {missingRequire} */ + goog.ui.Checkbox.State.UNDETERMINED))) { + checked = + (/** @suppress {missingRequire} */ + goog.ui.Checkbox.State.UNDETERMINED); + } else if ( + goog.array.contains( + classes, this.getClassForCheckboxState( + /** @suppress {missingRequire} */ goog.ui.Checkbox.State + .CHECKED))) { + checked = /** @suppress {missingRequire} */ goog.ui.Checkbox.State.CHECKED; + } else if (goog.array.contains(classes, + this.getClassForCheckboxState(/** @suppress {missingRequire} */ + goog.ui.Checkbox.State.UNCHECKED))) { + checked = + (/** @suppress {missingRequire} */ + goog.ui.Checkbox.State.UNCHECKED); + } + checkbox.setCheckedInternal(checked); + goog.asserts.assert(element, 'The element cannot be null.'); + goog.a11y.aria.setState( + element, goog.a11y.aria.State.CHECKED, + this.ariaStateFromCheckState_(checked)); + + return element; +}; + + +/** + * Returns the ARIA role to be applied to checkboxes. + * @return {goog.a11y.aria.Role} ARIA role. + * @override + */ +goog.ui.CheckboxRenderer.prototype.getAriaRole = function() { + return goog.a11y.aria.Role.CHECKBOX; +}; + + +/** + * Updates the appearance of the control in response to a checkbox state + * change. + * @param {Element} element Checkbox element. + * @param {goog.ui.Checkbox.State} state Updated checkbox state. + */ +goog.ui.CheckboxRenderer.prototype.setCheckboxState = function(element, state) { + if (element) { + goog.asserts.assert(element); + var classToAdd = this.getClassForCheckboxState(state); + goog.asserts.assert(classToAdd); + goog.asserts.assert(element); + if (goog.dom.classlist.contains(element, classToAdd)) { + return; + } + goog.object.forEach( + /** @suppress {missingRequire} */ goog.ui.Checkbox.State, + function(state) { + var className = this.getClassForCheckboxState(state); + goog.asserts.assert(element); + goog.dom.classlist.enable( + element, className, className == classToAdd); + }, + this); + goog.a11y.aria.setState( + element, goog.a11y.aria.State.CHECKED, + this.ariaStateFromCheckState_(state)); + } +}; + + +/** + * Gets the checkbox's ARIA (accessibility) state from its checked state. + * @param {goog.ui.Checkbox.State} state Checkbox state. + * @return {string} The value of goog.a11y.aria.state.CHECKED. Either 'true', + * 'false', or 'mixed'. + * @private + */ +goog.ui.CheckboxRenderer.prototype.ariaStateFromCheckState_ = function(state) { + if (state == + /** @suppress {missingRequire} */ goog.ui.Checkbox.State.UNDETERMINED) { + return 'mixed'; + } else if ( + state == + /** @suppress {missingRequire} */ goog.ui.Checkbox.State.CHECKED) { + return 'true'; + } else { + return 'false'; + } +}; + + +/** @override */ +goog.ui.CheckboxRenderer.prototype.getCssClass = function() { + return goog.ui.CheckboxRenderer.CSS_CLASS; +}; + + +/** + * Takes a single {@link goog.ui.Checkbox.State}, and returns the + * corresponding CSS class name. + * @param {goog.ui.Checkbox.State} state Checkbox state. + * @return {string} CSS class representing the given state. + * @protected + * @suppress {missingRequire} goog.ui.Checkbox + */ +goog.ui.CheckboxRenderer.prototype.getClassForCheckboxState = function(state) { + var baseClass = this.getStructuralCssClass(); + if (state == goog.ui.Checkbox.State.CHECKED) { + return goog.getCssName(baseClass, 'checked'); + } else if (state == goog.ui.Checkbox.State.UNCHECKED) { + return goog.getCssName(baseClass, 'unchecked'); + } else if (state == goog.ui.Checkbox.State.UNDETERMINED) { + return goog.getCssName(baseClass, 'undetermined'); + } + throw new Error('Invalid checkbox state: ' + state); +}; diff --git a/closure-library/closure/goog/ui/colormenubutton.js b/closure-library/closure/goog/ui/colormenubutton.js new file mode 100644 index 0000000000..accf4d1c73 --- /dev/null +++ b/closure-library/closure/goog/ui/colormenubutton.js @@ -0,0 +1,207 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A color menu button. Extends {@link goog.ui.MenuButton} by + * showing the currently selected color in the button caption. + * + * @author robbyw@google.com (Robby Walker) + * @author attila@google.com (Attila Bodis) + */ + +goog.provide('goog.ui.ColorMenuButton'); + +goog.require('goog.array'); +goog.require('goog.object'); +goog.require('goog.ui.ColorMenuButtonRenderer'); +goog.require('goog.ui.ColorPalette'); +goog.require('goog.ui.Component'); +goog.require('goog.ui.Menu'); +goog.require('goog.ui.MenuButton'); +goog.require('goog.ui.registry'); + + + +/** + * A color menu button control. Extends {@link goog.ui.MenuButton} by adding + * an API for getting and setting the currently selected color from a menu of + * color palettes. + * + * @param {goog.ui.ControlContent} content Text caption or existing DOM + * structure to display as the button's caption. + * @param {goog.ui.Menu=} opt_menu Menu to render under the button when clicked; + * should contain at least one {@link goog.ui.ColorPalette} if present. + * @param {goog.ui.MenuButtonRenderer=} opt_renderer Button renderer; + * defaults to {@link goog.ui.ColorMenuButtonRenderer}. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for + * document interaction. + * @constructor + * @extends {goog.ui.MenuButton} + */ +goog.ui.ColorMenuButton = function( + content, opt_menu, opt_renderer, opt_domHelper) { + goog.ui.MenuButton.call( + this, content, opt_menu, + opt_renderer || goog.ui.ColorMenuButtonRenderer.getInstance(), + opt_domHelper); +}; +goog.inherits(goog.ui.ColorMenuButton, goog.ui.MenuButton); + + +/** + * Default color palettes. + * @type {!Object} + */ +goog.ui.ColorMenuButton.PALETTES = { + /** Default grayscale colors. */ + GRAYSCALE: + ['#000', '#444', '#666', '#999', '#ccc', '#eee', '#f3f3f3', '#fff'], + + /** Default solid colors. */ + SOLID: ['#f00', '#f90', '#ff0', '#0f0', '#0ff', '#00f', '#90f', '#f0f'], + + /** Default pastel colors. */ + PASTEL: [ + '#f4cccc', '#fce5cd', '#fff2cc', '#d9ead3', '#d0e0e3', '#cfe2f3', '#d9d2e9', + '#ead1dc', '#ea9999', '#f9cb9c', '#ffe599', '#b6d7a8', '#a2c4c9', '#9fc5e8', + '#b4a7d6', '#d5a6bd', '#e06666', '#f6b26b', '#ffd966', '#93c47d', '#76a5af', + '#6fa8dc', '#8e7cc3', '#c27ba0', '#cc0000', '#e69138', '#f1c232', '#6aa84f', + '#45818e', '#3d85c6', '#674ea7', '#a64d79', '#990000', '#b45f06', '#bf9000', + '#38761d', '#134f5c', '#0b5394', '#351c75', '#741b47', '#660000', '#783f04', + '#7f6000', '#274e13', '#0c343d', '#073763', '#20124d', '#4c1130' + ] +}; + + +/** + * Value for the "no color" menu item object in the color menu (if present). + * The {@link goog.ui.ColorMenuButton#handleMenuAction} method interprets + * ACTION events dispatched by an item with this value as meaning "clear the + * selected color." + * @type {string} + */ +goog.ui.ColorMenuButton.NO_COLOR = 'none'; + + +/** + * Factory method that creates and returns a new {@link goog.ui.Menu} instance + * containing default color palettes. + * @param {Array=} opt_extraItems Optional extra menu items to + * add before the color palettes. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for + * document interaction. + * @return {!goog.ui.Menu} Color menu. + */ +goog.ui.ColorMenuButton.newColorMenu = function(opt_extraItems, opt_domHelper) { + var menu = new goog.ui.Menu(opt_domHelper); + + if (opt_extraItems) { + goog.array.forEach( + opt_extraItems, function(item) { menu.addChild(item, true); }); + } + + goog.object.forEach(goog.ui.ColorMenuButton.PALETTES, function(colors) { + var palette = new goog.ui.ColorPalette(colors, null, opt_domHelper); + palette.setSize(8); + menu.addChild(palette, true); + }); + + return menu; +}; + + +/** + * Returns the currently selected color (null if none). + * @return {string} The selected color. + */ +goog.ui.ColorMenuButton.prototype.getSelectedColor = function() { + return /** @type {string} */ (this.getValue()); +}; + + +/** + * Sets the selected color, or clears the selected color if the argument is + * null or not any of the available color choices. + * @param {?string} color New color. + */ +goog.ui.ColorMenuButton.prototype.setSelectedColor = function(color) { + this.setValue(color); +}; + + +/** + * Sets the value associated with the color menu button. Overrides + * {@link goog.ui.Button#setValue} by interpreting the value as a color + * spec string. + * @param {*} value New button value; should be a color spec string. + * @override + */ +goog.ui.ColorMenuButton.prototype.setValue = function(value) { + var color = /** @type {?string} */ (value); + for (var i = 0, item; item = this.getItemAt(i); i++) { + if (typeof item.setSelectedColor == 'function') { + // This menu item looks like a color palette. + item.setSelectedColor(color); + } + } + goog.ui.ColorMenuButton.superClass_.setValue.call(this, color); +}; + + +/** + * Handles {@link goog.ui.Component.EventType.ACTION} events dispatched by + * the menu item clicked by the user. Updates the button, calls the superclass + * implementation to hide the menu, stops the propagation of the event, and + * dispatches an ACTION event on behalf of the button itself. Overrides + * {@link goog.ui.MenuButton#handleMenuAction}. + * @param {goog.events.Event} e Action event to handle. + * @override + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.ColorMenuButton.prototype.handleMenuAction = function(e) { + if (typeof e.target.getSelectedColor == 'function') { + // User clicked something that looks like a color palette. + this.setValue(e.target.getSelectedColor()); + } else if (e.target.getValue() == goog.ui.ColorMenuButton.NO_COLOR) { + // User clicked the special "no color" menu item. + this.setValue(null); + } + goog.ui.ColorMenuButton.superClass_.handleMenuAction.call(this, e); + e.stopPropagation(); + this.dispatchEvent(goog.ui.Component.EventType.ACTION); +}; + + +/** + * Opens or closes the menu. Overrides {@link goog.ui.MenuButton#setOpen} by + * generating a default color menu on the fly if needed. + * @param {boolean} open Whether to open or close the menu. + * @param {goog.events.Event=} opt_e Mousedown event that caused the menu to + * be opened. + * @override + */ +goog.ui.ColorMenuButton.prototype.setOpen = function(open, opt_e) { + if (open && this.getItemCount() == 0) { + this.setMenu( + goog.ui.ColorMenuButton.newColorMenu(null, this.getDomHelper())); + this.setValue(/** @type {?string} */ (this.getValue())); + } + goog.ui.ColorMenuButton.superClass_.setOpen.call(this, open, opt_e); +}; + + +// Register a decorator factory function for goog.ui.ColorMenuButtons. +goog.ui.registry.setDecoratorByClassName( + goog.ui.ColorMenuButtonRenderer.CSS_CLASS, + function() { return new goog.ui.ColorMenuButton(null); }); diff --git a/closure-library/closure/goog/ui/colormenubuttonrenderer.js b/closure-library/closure/goog/ui/colormenubuttonrenderer.js new file mode 100644 index 0000000000..772b3ad090 --- /dev/null +++ b/closure-library/closure/goog/ui/colormenubuttonrenderer.js @@ -0,0 +1,150 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Renderer for {@link goog.ui.ColorMenuButton}s. + * + * @author robbyw@google.com (Robby Walker) + * @author attila@google.com (Attila Bodis) + */ + +goog.provide('goog.ui.ColorMenuButtonRenderer'); + +goog.require('goog.asserts'); +goog.require('goog.color'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.classlist'); +goog.require('goog.ui.MenuButtonRenderer'); +goog.require('goog.userAgent'); + + + +/** + * Renderer for {@link goog.ui.ColorMenuButton}s. + * @constructor + * @extends {goog.ui.MenuButtonRenderer} + */ +goog.ui.ColorMenuButtonRenderer = function() { + goog.ui.MenuButtonRenderer.call(this); +}; +goog.inherits(goog.ui.ColorMenuButtonRenderer, goog.ui.MenuButtonRenderer); +goog.addSingletonGetter(goog.ui.ColorMenuButtonRenderer); + + +/** + * Default CSS class to be applied to the root element of components rendered + * by this renderer. + * @type {string} + */ +goog.ui.ColorMenuButtonRenderer.CSS_CLASS = + goog.getCssName('goog-color-menu-button'); + + +/** + * Overrides the superclass implementation by wrapping the caption text or DOM + * structure in a color indicator element. Creates the following DOM structure: + * + *
      + *
      + * Contents... + *
      + *
      + * + * The 'goog-color-menu-button-indicator' style should be defined to have a + * bottom border of nonzero width and a default color that blends into its + * background. + * @param {goog.ui.ControlContent} content Text caption or DOM structure. + * @param {goog.dom.DomHelper} dom DOM helper, used for document interaction. + * @return {Element} Caption element. + * @override + */ +goog.ui.ColorMenuButtonRenderer.prototype.createCaption = function( + content, dom) { + return goog.ui.ColorMenuButtonRenderer.superClass_.createCaption.call( + this, goog.ui.ColorMenuButtonRenderer.wrapCaption(content, dom), dom); +}; + + +/** + * Wrap a caption in a div with the color-menu-button-indicator CSS class. + * @param {goog.ui.ControlContent} content Text caption or DOM structure. + * @param {goog.dom.DomHelper} dom DOM helper, used for document interaction. + * @return {!Element} Caption element. + */ +goog.ui.ColorMenuButtonRenderer.wrapCaption = function(content, dom) { + return dom.createDom( + goog.dom.TagName.DIV, + goog.getCssName(goog.ui.ColorMenuButtonRenderer.CSS_CLASS, 'indicator'), + content); +}; + + +/** + * Takes a color menu button control's root element and a value object + * (which is assumed to be a color), and updates the button's DOM to reflect + * the new color. Overrides {@link goog.ui.ButtonRenderer#setValue}. + * @param {Element} element The button control's root element (if rendered). + * @param {*} value New value; assumed to be a color spec string. + * @override + */ +goog.ui.ColorMenuButtonRenderer.prototype.setValue = function(element, value) { + if (element) { + goog.ui.ColorMenuButtonRenderer.setCaptionValue( + this.getContentElement(element), value); + } +}; + + +/** + * Takes a control's content element and a value object (which is assumed + * to be a color), and updates its DOM to reflect the new color. + * @param {Element} caption A content element of a control. + * @param {*} value New value; assumed to be a color spec string. + */ +goog.ui.ColorMenuButtonRenderer.setCaptionValue = function(caption, value) { + // Assume that the caption's first child is the indicator. + if (caption && caption.firstChild) { + // Normalize the value to a hex color spec or null (otherwise setting + // borderBottomColor will cause a JS error on IE). + var hexColor; + + var strValue = /** @type {string} */ (value); + hexColor = strValue && goog.color.isValidColor(strValue) ? + goog.color.parse(strValue).hex : + null; + + // Stupid IE6/7 doesn't do transparent borders. + // TODO(attila): Add user-agent version check when IE8 comes out... + caption.firstChild.style.borderBottomColor = + hexColor || (goog.userAgent.IE ? '' : 'transparent'); + } +}; + + +/** + * Initializes the button's DOM when it enters the document. Overrides the + * superclass implementation by making sure the button's color indicator is + * initialized. + * @param {goog.ui.Control} button goog.ui.ColorMenuButton whose DOM is to be + * initialized as it enters the document. + * @override + */ +goog.ui.ColorMenuButtonRenderer.prototype.initializeDom = function(button) { + var buttonElement = button.getElement(); + goog.asserts.assert(buttonElement); + this.setValue(buttonElement, button.getValue()); + goog.dom.classlist.add( + buttonElement, goog.ui.ColorMenuButtonRenderer.CSS_CLASS); + goog.ui.ColorMenuButtonRenderer.superClass_.initializeDom.call(this, button); +}; diff --git a/closure-library/closure/goog/ui/colorpalette.js b/closure-library/closure/goog/ui/colorpalette.js new file mode 100644 index 0000000000..4b70027bee --- /dev/null +++ b/closure-library/closure/goog/ui/colorpalette.js @@ -0,0 +1,180 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A control for representing a palette of colors, that the user + * can highlight or select via the keyboard or the mouse. + * + */ + +goog.provide('goog.ui.ColorPalette'); + +goog.require('goog.array'); +goog.require('goog.color'); +goog.require('goog.dom.TagName'); +goog.require('goog.style'); +goog.require('goog.ui.Palette'); +goog.require('goog.ui.PaletteRenderer'); + + + +/** + * A color palette is a grid of color swatches that the user can highlight or + * select via the keyboard or the mouse. The selection state of the palette is + * controlled by a selection model. When the user makes a selection, the + * component fires an ACTION event. Event listeners may retrieve the selected + * color using the {@link #getSelectedColor} method. + * + * @param {Array=} opt_colors Array of colors in any valid CSS color + * format. + * @param {goog.ui.PaletteRenderer=} opt_renderer Renderer used to render or + * decorate the palette; defaults to {@link goog.ui.PaletteRenderer}. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for + * document interaction. + * @constructor + * @extends {goog.ui.Palette} + */ +goog.ui.ColorPalette = function(opt_colors, opt_renderer, opt_domHelper) { + /** + * Array of colors to show in the palette. + * @type {Array} + * @private + */ + this.colors_ = opt_colors || []; + + goog.ui.Palette.call( + this, null, opt_renderer || goog.ui.PaletteRenderer.getInstance(), + opt_domHelper); + + // Set the colors separately from the super call since we need the correct + // DomHelper to be initialized for this class. + this.setColors(this.colors_); +}; +goog.inherits(goog.ui.ColorPalette, goog.ui.Palette); +goog.tagUnsealableClass(goog.ui.ColorPalette); + + +/** + * Array of normalized colors. Initialized lazily as often never needed. + * @type {?Array} + * @private + */ +goog.ui.ColorPalette.prototype.normalizedColors_ = null; + + +/** + * Array of labels for the colors. Will be used for the tooltips and + * accessibility. + * @type {?Array} + * @private + */ +goog.ui.ColorPalette.prototype.labels_ = null; + + +/** + * Returns the array of colors represented in the color palette. + * @return {Array} Array of colors. + */ +goog.ui.ColorPalette.prototype.getColors = function() { + return this.colors_; +}; + + +/** + * Sets the colors that are contained in the palette. + * @param {Array} colors Array of colors in any valid CSS color format. + * @param {Array=} opt_labels The array of labels to be used as + * tooltips. When not provided, the color value will be used. + */ +goog.ui.ColorPalette.prototype.setColors = function(colors, opt_labels) { + this.colors_ = colors; + this.labels_ = opt_labels || null; + this.normalizedColors_ = null; + this.setContent(this.createColorNodes()); +}; + + +/** + * @return {?string} The current selected color in hex, or null. + */ +goog.ui.ColorPalette.prototype.getSelectedColor = function() { + var selectedItem = /** @type {Element} */ (this.getSelectedItem()); + if (selectedItem) { + var color = goog.style.getStyle(selectedItem, 'background-color'); + return goog.ui.ColorPalette.parseColor_(color); + } else { + return null; + } +}; + + +/** + * Sets the selected color. Clears the selection if the argument is null or + * can't be parsed as a color. + * @param {?string} color The color to set as selected; null clears the + * selection. + */ +goog.ui.ColorPalette.prototype.setSelectedColor = function(color) { + var hexColor = goog.ui.ColorPalette.parseColor_(color); + if (!this.normalizedColors_) { + this.normalizedColors_ = goog.array.map(this.colors_, function(color) { + return goog.ui.ColorPalette.parseColor_(color); + }); + } + this.setSelectedIndex( + hexColor ? goog.array.indexOf(this.normalizedColors_, hexColor) : -1); +}; + + +/** + * @return {!Array} An array of DOM nodes for each color. + * @protected + */ +goog.ui.ColorPalette.prototype.createColorNodes = function() { + return goog.array.map(this.colors_, function(color, index) { + var swatch = this.getDomHelper().createDom(goog.dom.TagName.DIV, { + 'class': goog.getCssName(this.getRenderer().getCssClass(), 'colorswatch'), + 'style': 'background-color:' + color + }); + if (this.labels_ && this.labels_[index]) { + swatch.title = this.labels_[index]; + } else { + swatch.title = color.charAt(0) == '#' ? + 'RGB (' + goog.color.hexToRgb(color).join(', ') + ')' : + color; + } + return swatch; + }, this); +}; + + +/** + * Takes a string, attempts to parse it as a color spec, and returns a + * normalized hex color spec if successful (null otherwise). + * @param {?string} color String possibly containing a color spec; may be null. + * @return {?string} Normalized hex color spec, or null if the argument can't + * be parsed as a color. + * @private + */ +goog.ui.ColorPalette.parseColor_ = function(color) { + if (color) { + + try { + return goog.color.parse(color).hex; + } catch (ex) { + // Fall through. + } + } + return null; +}; diff --git a/closure-library/closure/goog/ui/colorpicker.js b/closure-library/closure/goog/ui/colorpicker.js new file mode 100644 index 0000000000..a4284bb082 --- /dev/null +++ b/closure-library/closure/goog/ui/colorpicker.js @@ -0,0 +1,345 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A color picker component. A color picker can compose several + * instances of goog.ui.ColorPalette. + * + * NOTE: The ColorPicker is in a state of transition towards the common + * component/control/container interface we are developing. If the API changes + * we will do our best to update your code. The end result will be that a + * color picker will compose multiple color palettes. In the simple case this + * will be one grid, but may consistute 3 distinct grids, a custom color picker + * or even a color wheel. + * + */ + +goog.provide('goog.ui.ColorPicker'); +goog.provide('goog.ui.ColorPicker.EventType'); + +goog.require('goog.ui.ColorPalette'); +goog.require('goog.ui.Component'); + + + +/** + * Create a new, empty color picker. + * + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper. + * @param {goog.ui.ColorPalette=} opt_colorPalette Optional color palette to + * use for this color picker. + * @extends {goog.ui.Component} + * @constructor + * @final + */ +goog.ui.ColorPicker = function(opt_domHelper, opt_colorPalette) { + goog.ui.Component.call(this, opt_domHelper); + + /** + * The color palette used inside the color picker. + * @type {goog.ui.ColorPalette?} + * @private + */ + this.colorPalette_ = opt_colorPalette || null; + + this.getHandler().listen( + this, goog.ui.Component.EventType.ACTION, this.onColorPaletteAction_); +}; +goog.inherits(goog.ui.ColorPicker, goog.ui.Component); + + +/** + * Default number of columns in the color palette. May be overridden by calling + * setSize. + * + * @type {number} + */ +goog.ui.ColorPicker.DEFAULT_NUM_COLS = 5; + + +/** + * Constants for event names. + * @enum {string} + */ +goog.ui.ColorPicker.EventType = { + CHANGE: 'change' +}; + + +/** + * Whether the component is focusable. + * @type {boolean} + * @private + */ +goog.ui.ColorPicker.prototype.focusable_ = true; + + +/** + * Gets the array of colors displayed by the color picker. + * Modifying this array will lead to unexpected behavior. + * @return {Array?} The colors displayed by this widget. + */ +goog.ui.ColorPicker.prototype.getColors = function() { + return this.colorPalette_ ? this.colorPalette_.getColors() : null; +}; + + +/** + * Sets the array of colors to be displayed by the color picker. + * @param {Array} colors The array of colors to be added. + */ +goog.ui.ColorPicker.prototype.setColors = function(colors) { + // TODO(user): Don't add colors directly, we should add palettes and the + // picker should support multiple palettes. + if (!this.colorPalette_) { + this.createColorPalette_(colors); + } else { + this.colorPalette_.setColors(colors); + } +}; + + +/** + * Sets the array of colors to be displayed by the color picker. + * @param {Array} colors The array of colors to be added. + * @deprecated Use setColors. + */ +goog.ui.ColorPicker.prototype.addColors = function(colors) { + this.setColors(colors); +}; + + +/** + * Sets the size of the palette. Will throw an error after the picker has been + * rendered. + * @param {goog.math.Size|number} size The size of the grid. + */ +goog.ui.ColorPicker.prototype.setSize = function(size) { + // TODO(user): The color picker should contain multiple palettes which will + // all be resized at this point. + if (!this.colorPalette_) { + this.createColorPalette_([]); + } + this.colorPalette_.setSize(size); +}; + + +/** + * Gets the number of columns displayed. + * @return {goog.math.Size?} The size of the grid. + */ +goog.ui.ColorPicker.prototype.getSize = function() { + return this.colorPalette_ ? this.colorPalette_.getSize() : null; +}; + + +/** + * Sets the number of columns. Will throw an error after the picker has been + * rendered. + * @param {number} n The number of columns. + * @deprecated Use setSize. + */ +goog.ui.ColorPicker.prototype.setColumnCount = function(n) { + this.setSize(n); +}; + + +/** + * @return {number} The index of the color selected. + */ +goog.ui.ColorPicker.prototype.getSelectedIndex = function() { + return this.colorPalette_ ? this.colorPalette_.getSelectedIndex() : -1; +}; + + +/** + * Sets which color is selected. A value that is out-of-range means that no + * color is selected. + * @param {number} ind The index in this.colors_ of the selected color. + */ +goog.ui.ColorPicker.prototype.setSelectedIndex = function(ind) { + if (this.colorPalette_) { + this.colorPalette_.setSelectedIndex(ind); + } +}; + + +/** + * Gets the color that is currently selected in this color picker. + * @return {?string} The hex string of the color selected, or null if no + * color is selected. + */ +goog.ui.ColorPicker.prototype.getSelectedColor = function() { + return this.colorPalette_ ? this.colorPalette_.getSelectedColor() : null; +}; + + +/** + * Sets which color is selected. Noop if the color palette hasn't been created + * yet. + * @param {string} color The selected color. + */ +goog.ui.ColorPicker.prototype.setSelectedColor = function(color) { + // TODO(user): This will set the color in the first available palette that + // contains it + if (this.colorPalette_) { + this.colorPalette_.setSelectedColor(color); + } +}; + + +/** + * Returns true if the component is focusable, false otherwise. The default + * is true. Focusable components always have a tab index and allocate a key + * handler to handle keyboard events while focused. + * @return {boolean} True iff the component is focusable. + */ +goog.ui.ColorPicker.prototype.isFocusable = function() { + return this.focusable_; +}; + + +/** + * Sets whether the component is focusable. The default is true. + * Focusable components always have a tab index and allocate a key handler to + * handle keyboard events while focused. + * @param {boolean} focusable True iff the component is focusable. + */ +goog.ui.ColorPicker.prototype.setFocusable = function(focusable) { + this.focusable_ = focusable; + if (this.colorPalette_) { + this.colorPalette_.setSupportedState( + goog.ui.Component.State.FOCUSED, focusable); + } +}; + + +/** + * ColorPickers cannot be used to decorate pre-existing html, since the + * structure they build is fairly complicated. + * @param {Element} element Element to decorate. + * @return {boolean} Returns always false. + * @override + */ +goog.ui.ColorPicker.prototype.canDecorate = function(element) { + return false; +}; + + +/** + * Renders the color picker inside the provided element. This will override the + * current content of the element. + * @override + */ +goog.ui.ColorPicker.prototype.enterDocument = function() { + goog.ui.ColorPicker.superClass_.enterDocument.call(this); + if (this.colorPalette_) { + this.colorPalette_.render(this.getElement()); + } + this.getElement().unselectable = 'on'; +}; + + +/** @override */ +goog.ui.ColorPicker.prototype.disposeInternal = function() { + goog.ui.ColorPicker.superClass_.disposeInternal.call(this); + if (this.colorPalette_) { + this.colorPalette_.dispose(); + this.colorPalette_ = null; + } +}; + + +/** + * Sets the focus to the color picker's palette. + */ +goog.ui.ColorPicker.prototype.focus = function() { + if (this.colorPalette_) { + this.colorPalette_.getElement().focus(); + } +}; + + +/** + * Handles actions from the color palette. + * + * @param {goog.events.Event} e The event. + * @private + */ +goog.ui.ColorPicker.prototype.onColorPaletteAction_ = function(e) { + e.stopPropagation(); + this.dispatchEvent(goog.ui.ColorPicker.EventType.CHANGE); +}; + + +/** + * Create a color palette for the color picker. + * @param {Array} colors Array of colors. + * @private + */ +goog.ui.ColorPicker.prototype.createColorPalette_ = function(colors) { + // TODO(user): The color picker should eventually just contain a number of + // palettes and manage the interactions between them. This will go away then. + var cp = new goog.ui.ColorPalette(colors, null, this.getDomHelper()); + cp.setSize(goog.ui.ColorPicker.DEFAULT_NUM_COLS); + cp.setSupportedState(goog.ui.Component.State.FOCUSED, this.focusable_); + // TODO(user): Use addChild(cp, true) and remove calls to render. + this.addChild(cp); + this.colorPalette_ = cp; + if (this.isInDocument()) { + this.colorPalette_.render(this.getElement()); + } +}; + + +/** + * Returns an unrendered instance of the color picker. The colors and layout + * are a simple color grid, the same as the old Gmail color picker. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper. + * @return {!goog.ui.ColorPicker} The unrendered instance. + */ +goog.ui.ColorPicker.createSimpleColorGrid = function(opt_domHelper) { + var cp = new goog.ui.ColorPicker(opt_domHelper); + cp.setSize(7); + cp.setColors(goog.ui.ColorPicker.SIMPLE_GRID_COLORS); + return cp; +}; + + +/** + * Array of colors for a 7-cell wide simple-grid color picker. + * @type {Array} + */ +goog.ui.ColorPicker.SIMPLE_GRID_COLORS = [ + // grays + '#ffffff', '#cccccc', '#c0c0c0', '#999999', '#666666', '#333333', '#000000', + // reds + '#ffcccc', '#ff6666', '#ff0000', '#cc0000', '#990000', '#660000', '#330000', + // oranges + '#ffcc99', '#ff9966', '#ff9900', '#ff6600', '#cc6600', '#993300', '#663300', + // yellows + '#ffff99', '#ffff66', '#ffcc66', '#ffcc33', '#cc9933', '#996633', '#663333', + // olives + '#ffffcc', '#ffff33', '#ffff00', '#ffcc00', '#999900', '#666600', '#333300', + // greens + '#99ff99', '#66ff99', '#33ff33', '#33cc00', '#009900', '#006600', '#003300', + // turquoises + '#99ffff', '#33ffff', '#66cccc', '#00cccc', '#339999', '#336666', '#003333', + // blues + '#ccffff', '#66ffff', '#33ccff', '#3366ff', '#3333ff', '#000099', '#000066', + // purples + '#ccccff', '#9999ff', '#6666cc', '#6633ff', '#6600cc', '#333399', '#330099', + // violets + '#ffccff', '#ff99ff', '#cc66cc', '#cc33cc', '#993399', '#663366', '#330033' +]; diff --git a/closure-library/closure/goog/ui/combobox.js b/closure-library/closure/goog/ui/combobox.js new file mode 100644 index 0000000000..c74f73c23e --- /dev/null +++ b/closure-library/closure/goog/ui/combobox.js @@ -0,0 +1,990 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A combo box control that allows user input with + * auto-suggestion from a limited set of options. + * + * @see ../demos/combobox.html + */ + +goog.provide('goog.ui.ComboBox'); +goog.provide('goog.ui.ComboBoxItem'); + +goog.require('goog.Timer'); +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.InputType'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.classlist'); +goog.require('goog.events.EventType'); +goog.require('goog.events.InputHandler'); +goog.require('goog.events.KeyCodes'); +goog.require('goog.events.KeyHandler'); +goog.require('goog.log'); +goog.require('goog.positioning.Corner'); +goog.require('goog.positioning.MenuAnchoredPosition'); +goog.require('goog.string'); +goog.require('goog.style'); +goog.require('goog.ui.Component'); +goog.require('goog.ui.ItemEvent'); +goog.require('goog.ui.LabelInput'); +goog.require('goog.ui.Menu'); +goog.require('goog.ui.MenuItem'); +goog.require('goog.ui.MenuSeparator'); +goog.require('goog.ui.registry'); +goog.require('goog.userAgent'); + + + +/** + * A ComboBox control. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper. + * @param {goog.ui.Menu=} opt_menu Optional menu component. + * This menu is disposed of by this control. + * @param {goog.ui.LabelInput=} opt_labelInput Optional label input. + * This label input is disposed of by this control. + * @extends {goog.ui.Component} + * @constructor + */ +goog.ui.ComboBox = function(opt_domHelper, opt_menu, opt_labelInput) { + goog.ui.Component.call(this, opt_domHelper); + + this.labelInput_ = opt_labelInput || new goog.ui.LabelInput(); + this.enabled_ = true; + + // TODO(user): Allow lazy creation of menus/menu items + this.menu_ = opt_menu || new goog.ui.Menu(this.getDomHelper()); + this.setupMenu_(); +}; +goog.inherits(goog.ui.ComboBox, goog.ui.Component); +goog.tagUnsealableClass(goog.ui.ComboBox); + + +/** + * Number of milliseconds to wait before dismissing combobox after blur. + * @type {number} + */ +goog.ui.ComboBox.BLUR_DISMISS_TIMER_MS = 250; + + +/** + * A logger to help debugging of combo box behavior. + * @type {goog.log.Logger} + * @private + */ +goog.ui.ComboBox.prototype.logger_ = goog.log.getLogger('goog.ui.ComboBox'); + + +/** + * Whether the combo box is enabled. + * @type {boolean} + * @private + */ +goog.ui.ComboBox.prototype.enabled_; + + +/** + * Keyboard event handler to manage key events dispatched by the input element. + * @type {goog.events.KeyHandler} + * @private + */ +goog.ui.ComboBox.prototype.keyHandler_; + + +/** + * Input handler to take care of firing events when the user inputs text in + * the input. + * @type {goog.events.InputHandler?} + * @private + */ +goog.ui.ComboBox.prototype.inputHandler_ = null; + + +/** + * The last input token. + * @type {?string} + * @private + */ +goog.ui.ComboBox.prototype.lastToken_ = null; + + +/** + * A LabelInput control that manages the focus/blur state of the input box. + * @type {goog.ui.LabelInput?} + * @private + */ +goog.ui.ComboBox.prototype.labelInput_ = null; + + +/** + * Drop down menu for the combo box. Will be created at construction time. + * @type {goog.ui.Menu?} + * @private + */ +goog.ui.ComboBox.prototype.menu_ = null; + + +/** + * The cached visible count. + * @type {number} + * @private + */ +goog.ui.ComboBox.prototype.visibleCount_ = -1; + + +/** + * The input element. + * @type {Element} + * @private + */ +goog.ui.ComboBox.prototype.input_ = null; + + +/** + * The match function. The first argument for the match function will be + * a MenuItem's caption and the second will be the token to evaluate. + * @type {Function} + * @private + */ +goog.ui.ComboBox.prototype.matchFunction_ = goog.string.startsWith; + + +/** + * Element used as the combo boxes button. + * @type {Element} + * @private + */ +goog.ui.ComboBox.prototype.button_ = null; + + +/** + * Default text content for the input box when it is unchanged and unfocussed. + * @type {string} + * @private + */ +goog.ui.ComboBox.prototype.defaultText_ = ''; + + +/** + * Name for the input box created + * @type {string} + * @private + */ +goog.ui.ComboBox.prototype.fieldName_ = ''; + + +/** + * Timer identifier for delaying the dismissal of the combo menu. + * @type {?number} + * @private + */ +goog.ui.ComboBox.prototype.dismissTimer_ = null; + + +/** + * True if the unicode inverted triangle should be displayed in the dropdown + * button. Defaults to false. + * @type {boolean} useDropdownArrow + * @private + */ +goog.ui.ComboBox.prototype.useDropdownArrow_ = false; + + +/** + * Create the DOM objects needed for the combo box. A span and text input. + * @override + */ +goog.ui.ComboBox.prototype.createDom = function() { + this.input_ = this.getDomHelper().createDom(goog.dom.TagName.INPUT, { + name: this.fieldName_, + type: goog.dom.InputType.TEXT, + autocomplete: 'off' + }); + this.button_ = this.getDomHelper().createDom( + goog.dom.TagName.SPAN, goog.getCssName('goog-combobox-button')); + this.setElementInternal( + this.getDomHelper().createDom( + goog.dom.TagName.SPAN, goog.getCssName('goog-combobox'), this.input_, + this.button_)); + if (this.useDropdownArrow_) { + goog.dom.setTextContent(this.button_, '\u25BC'); + goog.style.setUnselectable(this.button_, true /* unselectable */); + } + this.input_.setAttribute('label', this.defaultText_); + this.labelInput_.decorate(this.input_); + this.menu_.setFocusable(false); + if (!this.menu_.isInDocument()) { + this.addChild(this.menu_, true); + } +}; + + +/** + * Enables/Disables the combo box. + * @param {boolean} enabled Whether to enable (true) or disable (false) the + * combo box. + */ +goog.ui.ComboBox.prototype.setEnabled = function(enabled) { + this.enabled_ = enabled; + this.labelInput_.setEnabled(enabled); + goog.dom.classlist.enable( + goog.asserts.assert(this.getElement()), + goog.getCssName('goog-combobox-disabled'), !enabled); +}; + + +/** + * @return {boolean} Whether the menu item is enabled. + */ +goog.ui.ComboBox.prototype.isEnabled = function() { + return this.enabled_; +}; + + +/** @override */ +goog.ui.ComboBox.prototype.enterDocument = function() { + goog.ui.ComboBox.superClass_.enterDocument.call(this); + + var handler = this.getHandler(); + handler.listen( + this.getElement(), goog.events.EventType.MOUSEDOWN, + this.onComboMouseDown_); + handler.listen( + this.getDomHelper().getDocument(), goog.events.EventType.MOUSEDOWN, + this.onDocClicked_); + + handler.listen(this.input_, goog.events.EventType.BLUR, this.onInputBlur_); + + this.keyHandler_ = new goog.events.KeyHandler(this.input_); + handler.listen( + this.keyHandler_, goog.events.KeyHandler.EventType.KEY, + this.handleKeyEvent); + + this.inputHandler_ = new goog.events.InputHandler(this.input_); + handler.listen( + this.inputHandler_, goog.events.InputHandler.EventType.INPUT, + this.onInputEvent_); + + handler.listen( + this.menu_, goog.ui.Component.EventType.ACTION, this.onMenuSelected_); +}; + + +/** @override */ +goog.ui.ComboBox.prototype.exitDocument = function() { + this.keyHandler_.dispose(); + delete this.keyHandler_; + this.inputHandler_.dispose(); + this.inputHandler_ = null; + goog.ui.ComboBox.superClass_.exitDocument.call(this); +}; + + +/** + * Combo box currently can't decorate elements. + * @return {boolean} The value false. + * @override + */ +goog.ui.ComboBox.prototype.canDecorate = function() { + return false; +}; + + +/** @override */ +goog.ui.ComboBox.prototype.disposeInternal = function() { + goog.ui.ComboBox.superClass_.disposeInternal.call(this); + + this.clearDismissTimer_(); + + this.labelInput_.dispose(); + this.menu_.dispose(); + + this.labelInput_ = null; + this.menu_ = null; + this.input_ = null; + this.button_ = null; +}; + + +/** + * Dismisses the menu and resets the value of the edit field. + */ +goog.ui.ComboBox.prototype.dismiss = function() { + this.clearDismissTimer_(); + this.hideMenu_(); + this.menu_.setHighlightedIndex(-1); +}; + + +/** + * Adds a new menu item at the end of the menu. + * @param {goog.ui.MenuItem} item Menu item to add to the menu. + */ +goog.ui.ComboBox.prototype.addItem = function(item) { + this.menu_.addChild(item, true); + this.visibleCount_ = -1; +}; + + +/** + * Adds a new menu item at a specific index in the menu. + * @param {goog.ui.MenuItem} item Menu item to add to the menu. + * @param {number} n Index at which to insert the menu item. + */ +goog.ui.ComboBox.prototype.addItemAt = function(item, n) { + this.menu_.addChildAt(item, n, true); + this.visibleCount_ = -1; +}; + + +/** + * Removes an item from the menu and disposes it. + * @param {goog.ui.MenuItem} item The menu item to remove. + */ +goog.ui.ComboBox.prototype.removeItem = function(item) { + var child = this.menu_.removeChild(item, true); + if (child) { + child.dispose(); + this.visibleCount_ = -1; + } +}; + + +/** + * Remove all of the items from the ComboBox menu + */ +goog.ui.ComboBox.prototype.removeAllItems = function() { + for (var i = this.getItemCount() - 1; i >= 0; --i) { + this.removeItem(this.getItemAt(i)); + } +}; + + +/** + * Removes a menu item at a given index in the menu. + * @param {number} n Index of item. + */ +goog.ui.ComboBox.prototype.removeItemAt = function(n) { + var child = this.menu_.removeChildAt(n, true); + if (child) { + child.dispose(); + this.visibleCount_ = -1; + } +}; + + +/** + * Returns a reference to the menu item at a given index. + * @param {number} n Index of menu item. + * @return {goog.ui.MenuItem?} Reference to the menu item. + */ +goog.ui.ComboBox.prototype.getItemAt = function(n) { + return /** @type {goog.ui.MenuItem?} */ (this.menu_.getChildAt(n)); +}; + + +/** + * Returns the number of items in the list, including non-visible items, + * such as separators. + * @return {number} Number of items in the menu for this combobox. + */ +goog.ui.ComboBox.prototype.getItemCount = function() { + return this.menu_.getChildCount(); +}; + + +/** + * @return {goog.ui.Menu} The menu that pops up. + */ +goog.ui.ComboBox.prototype.getMenu = function() { + return this.menu_; +}; + + +/** + * @return {Element} The input element. + */ +goog.ui.ComboBox.prototype.getInputElement = function() { + return this.input_; +}; + + +/** + * @return {goog.ui.LabelInput} A LabelInput control that manages the + * focus/blur state of the input box. + */ +goog.ui.ComboBox.prototype.getLabelInput = function() { + return this.labelInput_; +}; + + +/** + * @return {number} The number of visible items in the menu. + * @private + */ +goog.ui.ComboBox.prototype.getNumberOfVisibleItems_ = function() { + if (this.visibleCount_ == -1) { + var count = 0; + for (var i = 0, n = this.menu_.getChildCount(); i < n; i++) { + var item = this.menu_.getChildAt(i); + if (!(item instanceof goog.ui.MenuSeparator) && item.isVisible()) { + count++; + } + } + this.visibleCount_ = count; + } + + return this.visibleCount_; +}; + + +/** + * Sets the match function to be used when filtering the combo box menu. + * @param {Function} matchFunction The match function to be used when filtering + * the combo box menu. + */ +goog.ui.ComboBox.prototype.setMatchFunction = function(matchFunction) { + this.matchFunction_ = matchFunction; +}; + + +/** + * @return {Function} The match function for the combox box. + */ +goog.ui.ComboBox.prototype.getMatchFunction = function() { + return this.matchFunction_; +}; + + +/** + * Sets the default text for the combo box. + * @param {string} text The default text for the combo box. + */ +goog.ui.ComboBox.prototype.setDefaultText = function(text) { + this.defaultText_ = text; + if (this.labelInput_) { + this.labelInput_.setLabel(this.defaultText_); + } +}; + + +/** + * @return {string} text The default text for the combox box. + */ +goog.ui.ComboBox.prototype.getDefaultText = function() { + return this.defaultText_; +}; + + +/** + * Sets the field name for the combo box. + * @param {string} fieldName The field name for the combo box. + */ +goog.ui.ComboBox.prototype.setFieldName = function(fieldName) { + this.fieldName_ = fieldName; +}; + + +/** + * @return {string} The field name for the combo box. + */ +goog.ui.ComboBox.prototype.getFieldName = function() { + return this.fieldName_; +}; + + +/** + * Set to true if a unicode inverted triangle should be displayed in the + * dropdown button. + * This option defaults to false for backwards compatibility. + * @param {boolean} useDropdownArrow True to use the dropdown arrow. + */ +goog.ui.ComboBox.prototype.setUseDropdownArrow = function(useDropdownArrow) { + this.useDropdownArrow_ = !!useDropdownArrow; +}; + + +/** + * Sets the current value of the combo box. + * @param {string} value The new value. + */ +goog.ui.ComboBox.prototype.setValue = function(value) { + if (this.labelInput_.getValue() != value) { + this.labelInput_.setValue(value); + this.handleInputChange_(); + } +}; + + +/** + * @return {string} The current value of the combo box. + */ +goog.ui.ComboBox.prototype.getValue = function() { + return this.labelInput_.getValue(); +}; + + +/** + * @return {string} HTML escaped token. + */ +goog.ui.ComboBox.prototype.getToken = function() { + // TODO(user): Remove HTML escaping and fix the existing calls. + return goog.string.htmlEscape(this.getTokenText_()); +}; + + +/** + * @return {string} The token for the current cursor position in the + * input box, when multi-input is disabled it will be the full input value. + * @private + */ +goog.ui.ComboBox.prototype.getTokenText_ = function() { + // TODO(user): Implement multi-input such that getToken returns a substring + // of the whole input delimited by commas. + return goog.string.trim(this.labelInput_.getValue().toLowerCase()); +}; + + +/** + * @private + */ +goog.ui.ComboBox.prototype.setupMenu_ = function() { + var sm = this.menu_; + sm.setVisible(false); + sm.setAllowAutoFocus(false); + sm.setAllowHighlightDisabled(true); +}; + + +/** + * Shows the menu if it isn't already showing. Also positions the menu + * correctly, resets the menu item visibilities and highlights the relevant + * item. + * @param {boolean} showAll Whether to show all items, with the first matching + * item highlighted. + * @private + */ +goog.ui.ComboBox.prototype.maybeShowMenu_ = function(showAll) { + var isVisible = this.menu_.isVisible(); + var numVisibleItems = this.getNumberOfVisibleItems_(); + + if (isVisible && numVisibleItems == 0) { + goog.log.fine(this.logger_, 'no matching items, hiding'); + this.hideMenu_(); + + } else if (!isVisible && numVisibleItems > 0) { + if (showAll) { + goog.log.fine(this.logger_, 'showing menu'); + this.setItemVisibilityFromToken_(''); + this.setItemHighlightFromToken_(this.getTokenText_()); + } + // In Safari 2.0, when clicking on the combox box, the blur event is + // received after the click event that invokes this function. Since we want + // to cancel the dismissal after the blur event is processed, we have to + // wait for all event processing to happen. + goog.Timer.callOnce(this.clearDismissTimer_, 1, this); + + this.showMenu_(); + } + + this.positionMenu(); +}; + + +/** + * Positions the menu. + * @protected + */ +goog.ui.ComboBox.prototype.positionMenu = function() { + if (this.menu_ && this.menu_.isVisible()) { + var position = new goog.positioning.MenuAnchoredPosition( + this.getElement(), goog.positioning.Corner.BOTTOM_START, true); + position.reposition( + this.menu_.getElement(), goog.positioning.Corner.TOP_START); + } +}; + + +/** + * Show the menu and add an active class to the combo box's element. + * @private + */ +goog.ui.ComboBox.prototype.showMenu_ = function() { + this.menu_.setVisible(true); + goog.dom.classlist.add( + goog.asserts.assert(this.getElement()), + goog.getCssName('goog-combobox-active')); +}; + + +/** + * Hide the menu and remove the active class from the combo box's element. + * @private + */ +goog.ui.ComboBox.prototype.hideMenu_ = function() { + this.menu_.setVisible(false); + goog.dom.classlist.remove( + goog.asserts.assert(this.getElement()), + goog.getCssName('goog-combobox-active')); +}; + + +/** + * Clears the dismiss timer if it's active. + * @private + */ +goog.ui.ComboBox.prototype.clearDismissTimer_ = function() { + if (this.dismissTimer_) { + goog.Timer.clear(this.dismissTimer_); + this.dismissTimer_ = null; + } +}; + + +/** + * Event handler for when the combo box area has been clicked. + * @param {goog.events.BrowserEvent} e The browser event. + * @private + */ +goog.ui.ComboBox.prototype.onComboMouseDown_ = function(e) { + // We only want this event on the element itself or the input or the button. + if (this.enabled_ && + (e.target == this.getElement() || e.target == this.input_ || + goog.dom.contains(this.button_, /** @type {Node} */ (e.target)))) { + if (this.menu_.isVisible()) { + goog.log.fine(this.logger_, 'Menu is visible, dismissing'); + this.dismiss(); + } else { + goog.log.fine(this.logger_, 'Opening dropdown'); + this.maybeShowMenu_(true); + if (goog.userAgent.OPERA) { + // select() doesn't focus elements in Opera. + this.input_.focus(); + } + this.input_.select(); + this.menu_.setMouseButtonPressed(true); + // Stop the click event from stealing focus + e.preventDefault(); + } + } + // Stop the event from propagating outside of the combo box + e.stopPropagation(); +}; + + +/** + * Event handler for when the document is clicked. + * @param {goog.events.BrowserEvent} e The browser event. + * @private + */ +goog.ui.ComboBox.prototype.onDocClicked_ = function(e) { + if (!goog.dom.contains( + this.menu_.getElement(), /** @type {Node} */ (e.target))) { + this.dismiss(); + } +}; + + +/** + * Handle the menu's select event. + * @param {goog.events.Event} e The event. + * @private + */ +goog.ui.ComboBox.prototype.onMenuSelected_ = function(e) { + var item = /** @type {!goog.ui.MenuItem} */ (e.target); + // Stop propagation of the original event and redispatch to allow the menu + // select to be cancelled at this level. i.e. if a menu item should cause + // some behavior such as a user prompt instead of assigning the caption as + // the value. + if (this.dispatchEvent( + new goog.ui.ItemEvent( + goog.ui.Component.EventType.ACTION, this, item))) { + var caption = item.getCaption(); + goog.log.fine( + this.logger_, 'Menu selection: ' + caption + '. Dismissing menu'); + if (this.labelInput_.getValue() != caption) { + this.labelInput_.setValue(caption); + this.dispatchEvent(goog.ui.Component.EventType.CHANGE); + } + this.dismiss(); + } + e.stopPropagation(); +}; + + +/** + * Event handler for when the input box looses focus -- hide the menu + * @param {goog.events.BrowserEvent} e The browser event. + * @private + */ +goog.ui.ComboBox.prototype.onInputBlur_ = function(e) { + this.clearDismissTimer_(); + this.dismissTimer_ = goog.Timer.callOnce( + this.dismiss, goog.ui.ComboBox.BLUR_DISMISS_TIMER_MS, this); +}; + + +/** + * Handles keyboard events from the input box. Returns true if the combo box + * was able to handle the event, false otherwise. + * @param {goog.events.KeyEvent} e Key event to handle. + * @return {boolean} Whether the event was handled by the combo box. + * @protected + * @suppress {visibility} performActionInternal + */ +goog.ui.ComboBox.prototype.handleKeyEvent = function(e) { + var isMenuVisible = this.menu_.isVisible(); + + // Give the menu a chance to handle the event. + if (isMenuVisible && this.menu_.handleKeyEvent(e)) { + return true; + } + + // The menu is either hidden or didn't handle the event. + var handled = false; + switch (e.keyCode) { + case goog.events.KeyCodes.ESC: + // If the menu is visible and the user hit Esc, dismiss the menu. + if (isMenuVisible) { + goog.log.fine( + this.logger_, 'Dismiss on Esc: ' + this.labelInput_.getValue()); + this.dismiss(); + handled = true; + } + break; + case goog.events.KeyCodes.TAB: + // If the menu is open and an option is highlighted, activate it. + if (isMenuVisible) { + var highlighted = this.menu_.getHighlighted(); + if (highlighted) { + goog.log.fine( + this.logger_, 'Select on Tab: ' + this.labelInput_.getValue()); + highlighted.performActionInternal(e); + handled = true; + } + } + break; + case goog.events.KeyCodes.UP: + case goog.events.KeyCodes.DOWN: + // If the menu is hidden and the user hit the up/down arrow, show it. + if (!isMenuVisible) { + goog.log.fine(this.logger_, 'Up/Down - maybe show menu'); + this.maybeShowMenu_(true); + handled = true; + } + break; + } + + if (handled) { + e.preventDefault(); + } + + return handled; +}; + + +/** + * Handles the content of the input box changing. + * @param {goog.events.Event} e The INPUT event to handle. + * @private + */ +goog.ui.ComboBox.prototype.onInputEvent_ = function(e) { + // If the key event is text-modifying, update the menu. + goog.log.fine( + this.logger_, 'Key is modifying: ' + this.labelInput_.getValue()); + this.handleInputChange_(); +}; + + +/** + * Handles the content of the input box changing, either because of user + * interaction or programmatic changes. + * @private + */ +goog.ui.ComboBox.prototype.handleInputChange_ = function() { + var token = this.getTokenText_(); + this.setItemVisibilityFromToken_(token); + if (goog.dom.getActiveElement(this.getDomHelper().getDocument()) == + this.input_) { + // Do not alter menu visibility unless the user focus is currently on the + // combobox (otherwise programmatic changes may cause the menu to become + // visible). + this.maybeShowMenu_(false); + } + var highlighted = this.menu_.getHighlighted(); + if (token == '' || !highlighted || !highlighted.isVisible()) { + this.setItemHighlightFromToken_(token); + } + this.lastToken_ = token; + this.dispatchEvent(goog.ui.Component.EventType.CHANGE); +}; + + +/** + * Loops through all menu items setting their visibility according to a token. + * @param {string} token The token. + * @private + */ +goog.ui.ComboBox.prototype.setItemVisibilityFromToken_ = function(token) { + var isVisibleItem = false; + var count = 0; + var recheckHidden = !this.matchFunction_(token, this.lastToken_); + + for (var i = 0, n = this.menu_.getChildCount(); i < n; i++) { + var item = this.menu_.getChildAt(i); + if (item instanceof goog.ui.MenuSeparator) { + // Ensure that separators are only shown if there is at least one visible + // item before them. + item.setVisible(isVisibleItem); + isVisibleItem = false; + } else if (item instanceof goog.ui.MenuItem) { + if (!item.isVisible() && !recheckHidden) continue; + + var caption = item.getCaption(); + var visible = this.isItemSticky_(item) || + caption && this.matchFunction_(caption.toLowerCase(), token); + if (typeof item.setFormatFromToken == 'function') { + item.setFormatFromToken(token); + } + item.setVisible(!!visible); + isVisibleItem = visible || isVisibleItem; + + } else { + // Assume all other items are correctly using their visibility. + isVisibleItem = item.isVisible() || isVisibleItem; + } + + if (!(item instanceof goog.ui.MenuSeparator) && item.isVisible()) { + count++; + } + } + + this.visibleCount_ = count; +}; + + +/** + * Highlights the first token that matches the given token. + * @param {string} token The token. + * @private + */ +goog.ui.ComboBox.prototype.setItemHighlightFromToken_ = function(token) { + if (token == '') { + this.menu_.setHighlightedIndex(-1); + return; + } + + for (var i = 0, n = this.menu_.getChildCount(); i < n; i++) { + var item = this.menu_.getChildAt(i); + var caption = item.getCaption(); + if (caption && this.matchFunction_(caption.toLowerCase(), token)) { + this.menu_.setHighlightedIndex(i); + if (item.setFormatFromToken) { + item.setFormatFromToken(token); + } + return; + } + } + this.menu_.setHighlightedIndex(-1); +}; + + +/** + * Returns true if the item has an isSticky method and the method returns true. + * @param {goog.ui.MenuItem} item The item. + * @return {boolean} Whether the item has an isSticky method and the method + * returns true. + * @private + */ +goog.ui.ComboBox.prototype.isItemSticky_ = function(item) { + return typeof item.isSticky == 'function' && item.isSticky(); +}; + + + +/** + * Class for combo box items. + * @param {goog.ui.ControlContent} content Text caption or DOM structure to + * display as the content of the item (use to add icons or styling to + * menus). + * @param {*=} opt_data Identifying data for the menu item. + * @param {goog.dom.DomHelper=} opt_domHelper Optional dom helper used for dom + * interactions. + * @param {goog.ui.MenuItemRenderer=} opt_renderer Optional renderer. + * @constructor + * @extends {goog.ui.MenuItem} + */ +goog.ui.ComboBoxItem = function( + content, opt_data, opt_domHelper, opt_renderer) { + goog.ui.ComboBoxItem.base( + this, 'constructor', content, opt_data, opt_domHelper, opt_renderer); +}; +goog.inherits(goog.ui.ComboBoxItem, goog.ui.MenuItem); +goog.tagUnsealableClass(goog.ui.ComboBoxItem); + + +// Register a decorator factory function for goog.ui.ComboBoxItems. +goog.ui.registry.setDecoratorByClassName( + goog.getCssName('goog-combobox-item'), function() { + // ComboBoxItem defaults to using MenuItemRenderer. + return new goog.ui.ComboBoxItem(null); + }); + + +/** + * Whether the menu item is sticky, non-sticky items will be hidden as the + * user types. + * @type {boolean} + * @private + */ +goog.ui.ComboBoxItem.prototype.isSticky_ = false; + + +/** + * Sets the menu item to be sticky or not sticky. + * @param {boolean} sticky Whether the menu item should be sticky. + */ +goog.ui.ComboBoxItem.prototype.setSticky = function(sticky) { + this.isSticky_ = sticky; +}; + + +/** + * @return {boolean} Whether the menu item is sticky. + */ +goog.ui.ComboBoxItem.prototype.isSticky = function() { + return this.isSticky_; +}; + + +/** + * Sets the format for a menu item based on a token, bolding the token. + * @param {string} token The token. + */ +goog.ui.ComboBoxItem.prototype.setFormatFromToken = function(token) { + if (this.isEnabled()) { + var caption = this.getCaption(); + var index = caption.toLowerCase().indexOf(token); + if (index >= 0) { + var domHelper = this.getDomHelper(); + this.setContent([ + domHelper.createTextNode(caption.substr(0, index)), + domHelper.createDom( + goog.dom.TagName.B, null, caption.substr(index, token.length)), + domHelper.createTextNode(caption.substr(index + token.length)) + ]); + } + } +}; diff --git a/closure-library/closure/goog/ui/component.js b/closure-library/closure/goog/ui/component.js new file mode 100644 index 0000000000..17ef048c7b --- /dev/null +++ b/closure-library/closure/goog/ui/component.js @@ -0,0 +1,1335 @@ +// Copyright 2007 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 Abstract class for all UI components. This defines the standard + * design pattern that all UI components should follow. + * + * @author attila@google.com (Attila Bodis) + * @see ../demos/samplecomponent.html + * @see http://code.google.com/p/closure-library/wiki/IntroToComponents + */ + +goog.provide('goog.ui.Component'); +goog.provide('goog.ui.Component.Error'); +goog.provide('goog.ui.Component.EventType'); +goog.provide('goog.ui.Component.State'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.TagName'); +goog.require('goog.events.EventHandler'); +goog.require('goog.events.EventTarget'); +goog.require('goog.object'); +goog.require('goog.style'); +goog.require('goog.ui.IdGenerator'); + + + +/** + * Default implementation of UI component. + * + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper. + * @constructor + * @extends {goog.events.EventTarget} + * @suppress {underscore} + */ +goog.ui.Component = function(opt_domHelper) { + goog.events.EventTarget.call(this); + /** + * DomHelper used to interact with the document, allowing components to be + * created in a different window. + * @protected {!goog.dom.DomHelper} + * @suppress {underscore|visibility} + */ + this.dom_ = opt_domHelper || goog.dom.getDomHelper(); + + /** + * Whether the component is rendered right-to-left. Right-to-left is set + * lazily when {@link #isRightToLeft} is called the first time, unless it has + * been set by calling {@link #setRightToLeft} explicitly. + * @private {?boolean} + */ + this.rightToLeft_ = goog.ui.Component.defaultRightToLeft_; + + /** + * Unique ID of the component, lazily initialized in {@link + * goog.ui.Component#getId} if needed. This property is strictly private and + * must not be accessed directly outside of this class! + * @private {?string} + */ + this.id_ = null; + + /** + * Whether the component is in the document. + * @private {boolean} + */ + this.inDocument_ = false; + + // TODO(attila): Stop referring to this private field in subclasses. + /** + * The DOM element for the component. + * @private {?Element} + */ + this.element_ = null; + + /** + * Event handler. + * TODO(user): rename it to handler_ after all component subclasses in + * inside Google have been cleaned up. + * Code search: http://go/component_code_search + * @private {goog.events.EventHandler|undefined} + */ + this.googUiComponentHandler_ = void 0; + + /** + * Arbitrary data object associated with the component. Such as meta-data. + * @private {*} + */ + this.model_ = null; + + /** + * Parent component to which events will be propagated. This property is + * strictly private and must not be accessed directly outside of this class! + * @private {goog.ui.Component?} + */ + this.parent_ = null; + + /** + * Array of child components. Lazily initialized on first use. Must be kept + * in sync with `childIndex_`. This property is strictly private and + * must not be accessed directly outside of this class! + * @private {?Array} + */ + this.children_ = null; + + /** + * Map of child component IDs to child components. Used for constant-time + * random access to child components by ID. Lazily initialized on first use. + * Must be kept in sync with `children_`. This property is strictly + * private and must not be accessed directly outside of this class! + * + * We use a plain Object, not a {@link goog.structs.Map}, for simplicity. + * This means components can't have children with IDs such as 'constructor' or + * 'valueOf', but this shouldn't really be an issue in practice, and if it is, + * we can always fix it later without changing the API. + * + * @private {?Object} + */ + this.childIndex_ = null; + + /** + * Flag used to keep track of whether a component decorated an already + * existing element or whether it created the DOM itself. + * + * If an element is decorated, dispose will leave the node in the document. + * It is up to the app to remove the node. + * + * If an element was rendered, dispose will remove the node automatically. + * + * @private {boolean} + */ + this.wasDecorated_ = false; + + /** + * If true, listen for PointerEvent types rather than MouseEvent types. This + * allows supporting drag gestures for touch/stylus input. + * @private {boolean} + */ + this.pointerEventsEnabled_ = false; +}; +goog.inherits(goog.ui.Component, goog.events.EventTarget); + + +/** + * @define {boolean} Whether to support calling decorate with an element that is + * not yet in the document. If true, we check if the element is in the + * document, and avoid calling enterDocument if it isn't. If false, we + * maintain legacy behavior (always call enterDocument from decorate). + */ +goog.define('goog.ui.Component.ALLOW_DETACHED_DECORATION', false); + + +/** + * Generator for unique IDs. + * @type {goog.ui.IdGenerator} + * @private + */ +goog.ui.Component.prototype.idGenerator_ = goog.ui.IdGenerator.getInstance(); + + +// TODO(gboyer): See if we can remove this and just check goog.i18n.bidi.IS_RTL. +/** + * @define {number} Defines the default BIDI directionality. + * 0: Unknown. + * 1: Left-to-right. + * -1: Right-to-left. + */ +goog.define('goog.ui.Component.DEFAULT_BIDI_DIR', 0); + + +/** + * The default right to left value. + * @type {?boolean} + * @private + */ +goog.ui.Component.defaultRightToLeft_ = + (goog.ui.Component.DEFAULT_BIDI_DIR == 1) ? + false : + (goog.ui.Component.DEFAULT_BIDI_DIR == -1) ? true : null; + + +/** + * Common events fired by components so that event propagation is useful. Not + * all components are expected to dispatch or listen for all event types. + * Events dispatched before a state transition should be cancelable to prevent + * the corresponding state change. + * @enum {string} + */ +goog.ui.Component.EventType = { + /** Dispatched before the component becomes visible. */ + BEFORE_SHOW: 'beforeshow', + + /** + * Dispatched after the component becomes visible. + * NOTE(user): For goog.ui.Container, this actually fires before containers + * are shown. Use goog.ui.Container.EventType.AFTER_SHOW if you want an event + * that fires after a goog.ui.Container is shown. + */ + SHOW: 'show', + + /** Dispatched before the component becomes hidden. */ + HIDE: 'hide', + + /** Dispatched before the component becomes disabled. */ + DISABLE: 'disable', + + /** Dispatched before the component becomes enabled. */ + ENABLE: 'enable', + + /** Dispatched before the component becomes highlighted. */ + HIGHLIGHT: 'highlight', + + /** Dispatched before the component becomes un-highlighted. */ + UNHIGHLIGHT: 'unhighlight', + + /** Dispatched before the component becomes activated. */ + ACTIVATE: 'activate', + + /** Dispatched before the component becomes deactivated. */ + DEACTIVATE: 'deactivate', + + /** Dispatched before the component becomes selected. */ + SELECT: 'select', + + /** Dispatched before the component becomes un-selected. */ + UNSELECT: 'unselect', + + /** Dispatched before a component becomes checked. */ + CHECK: 'check', + + /** Dispatched before a component becomes un-checked. */ + UNCHECK: 'uncheck', + + /** Dispatched before a component becomes focused. */ + FOCUS: 'focus', + + /** Dispatched before a component becomes blurred. */ + BLUR: 'blur', + + /** Dispatched before a component is opened (expanded). */ + OPEN: 'open', + + /** Dispatched before a component is closed (collapsed). */ + CLOSE: 'close', + + /** Dispatched after a component is moused over. */ + ENTER: 'enter', + + /** Dispatched after a component is moused out of. */ + LEAVE: 'leave', + + /** Dispatched after the user activates the component. */ + ACTION: 'action', + + /** Dispatched after the external-facing state of a component is changed. */ + CHANGE: 'change' +}; + + +/** + * Errors thrown by the component. + * @enum {string} + */ +goog.ui.Component.Error = { + /** + * Error when a method is not supported. + */ + NOT_SUPPORTED: 'Method not supported', + + /** + * Error when the given element can not be decorated. + */ + DECORATE_INVALID: 'Invalid element to decorate', + + /** + * Error when the component is already rendered and another render attempt is + * made. + */ + ALREADY_RENDERED: 'Component already rendered', + + /** + * Error when an attempt is made to set the parent of a component in a way + * that would result in an inconsistent object graph. + */ + PARENT_UNABLE_TO_BE_SET: 'Unable to set parent component', + + /** + * Error when an attempt is made to add a child component at an out-of-bounds + * index. We don't support sparse child arrays. + */ + CHILD_INDEX_OUT_OF_BOUNDS: 'Child component index out of bounds', + + /** + * Error when an attempt is made to remove a child component from a component + * other than its parent. + */ + NOT_OUR_CHILD: 'Child is not in parent component', + + /** + * Error when an operation requiring DOM interaction is made when the + * component is not in the document + */ + NOT_IN_DOCUMENT: 'Operation not supported while component is not in document', + + /** + * Error when an invalid component state is encountered. + */ + STATE_INVALID: 'Invalid component state' +}; + + +/** + * Common component states. Components may have distinct appearance depending + * on what state(s) apply to them. Not all components are expected to support + * all states. + * @enum {number} + */ +goog.ui.Component.State = { + /** + * Union of all supported component states. + */ + ALL: 0xFF, + + /** + * Component is disabled. + * @see goog.ui.Component.EventType.DISABLE + * @see goog.ui.Component.EventType.ENABLE + */ + DISABLED: 0x01, + + /** + * Component is highlighted. + * @see goog.ui.Component.EventType.HIGHLIGHT + * @see goog.ui.Component.EventType.UNHIGHLIGHT + */ + HOVER: 0x02, + + /** + * Component is active (or "pressed"). + * @see goog.ui.Component.EventType.ACTIVATE + * @see goog.ui.Component.EventType.DEACTIVATE + */ + ACTIVE: 0x04, + + /** + * Component is selected. + * @see goog.ui.Component.EventType.SELECT + * @see goog.ui.Component.EventType.UNSELECT + */ + SELECTED: 0x08, + + /** + * Component is checked. + * @see goog.ui.Component.EventType.CHECK + * @see goog.ui.Component.EventType.UNCHECK + */ + CHECKED: 0x10, + + /** + * Component has focus. + * @see goog.ui.Component.EventType.FOCUS + * @see goog.ui.Component.EventType.BLUR + */ + FOCUSED: 0x20, + + /** + * Component is opened (expanded). Applies to tree nodes, menu buttons, + * submenus, zippys (zippies?), etc. + * @see goog.ui.Component.EventType.OPEN + * @see goog.ui.Component.EventType.CLOSE + */ + OPENED: 0x40 +}; + + +/** + * Static helper method; returns the type of event components are expected to + * dispatch when transitioning to or from the given state. + * @param {goog.ui.Component.State} state State to/from which the component + * is transitioning. + * @param {boolean} isEntering Whether the component is entering or leaving the + * state. + * @return {goog.ui.Component.EventType} Event type to dispatch. + */ +goog.ui.Component.getStateTransitionEvent = function(state, isEntering) { + switch (state) { + case goog.ui.Component.State.DISABLED: + return isEntering ? goog.ui.Component.EventType.DISABLE : + goog.ui.Component.EventType.ENABLE; + case goog.ui.Component.State.HOVER: + return isEntering ? goog.ui.Component.EventType.HIGHLIGHT : + goog.ui.Component.EventType.UNHIGHLIGHT; + case goog.ui.Component.State.ACTIVE: + return isEntering ? goog.ui.Component.EventType.ACTIVATE : + goog.ui.Component.EventType.DEACTIVATE; + case goog.ui.Component.State.SELECTED: + return isEntering ? goog.ui.Component.EventType.SELECT : + goog.ui.Component.EventType.UNSELECT; + case goog.ui.Component.State.CHECKED: + return isEntering ? goog.ui.Component.EventType.CHECK : + goog.ui.Component.EventType.UNCHECK; + case goog.ui.Component.State.FOCUSED: + return isEntering ? goog.ui.Component.EventType.FOCUS : + goog.ui.Component.EventType.BLUR; + case goog.ui.Component.State.OPENED: + return isEntering ? goog.ui.Component.EventType.OPEN : + goog.ui.Component.EventType.CLOSE; + default: + // Fall through. + } + + // Invalid state. + throw new Error(goog.ui.Component.Error.STATE_INVALID); +}; + + +/** + * Set the default right-to-left value. This causes all component's created from + * this point forward to have the given value. This is useful for cases where + * a given page is always in one directionality, avoiding unnecessary + * right to left determinations. + * @param {?boolean} rightToLeft Whether the components should be rendered + * right-to-left. Null iff components should determine their directionality. + */ +goog.ui.Component.setDefaultRightToLeft = function(rightToLeft) { + goog.ui.Component.defaultRightToLeft_ = rightToLeft; +}; + + +/** + * Gets the unique ID for the instance of this component. If the instance + * doesn't already have an ID, generates one on the fly. + * @return {string} Unique component ID. + */ +goog.ui.Component.prototype.getId = function() { + return this.id_ || (this.id_ = this.idGenerator_.getNextUniqueId()); +}; + + +/** + * Assigns an ID to this component instance. It is the caller's responsibility + * to guarantee that the ID is unique. If the component is a child of a parent + * component, then the parent component's child index is updated to reflect the + * new ID; this may throw an error if the parent already has a child with an ID + * that conflicts with the new ID. + * @param {string} id Unique component ID. + */ +goog.ui.Component.prototype.setId = function(id) { + if (this.parent_ && this.parent_.childIndex_) { + // Update the parent's child index. + goog.object.remove(this.parent_.childIndex_, this.id_); + goog.object.add(this.parent_.childIndex_, id, this); + } + + // Update the component ID. + this.id_ = id; +}; + + +/** + * Gets the component's element. + * @return {Element} The element for the component. + */ +goog.ui.Component.prototype.getElement = function() { + return this.element_; +}; + + +/** + * Gets the component's element. This differs from getElement in that + * it assumes that the element exists (i.e. the component has been + * rendered/decorated) and will cause an assertion error otherwise (if + * assertion is enabled). + * @return {!Element} The element for the component. + */ +goog.ui.Component.prototype.getElementStrict = function() { + var el = this.element_; + goog.asserts.assert( + el, 'Can not call getElementStrict before rendering/decorating.'); + return el; +}; + + +/** + * Sets the component's root element to the given element. Considered + * protected and final. + * + * This should generally only be called during createDom. Setting the element + * does not actually change which element is rendered, only the element that is + * associated with this UI component. + * + * This should only be used by subclasses and its associated renderers. + * + * @param {Element} element Root element for the component. + */ +goog.ui.Component.prototype.setElementInternal = function(element) { + this.element_ = element; +}; + + +/** + * Returns an array of all the elements in this component's DOM with the + * provided className. + * @param {string} className The name of the class to look for. + * @return {!IArrayLike} The items found with the class name provided. + */ +goog.ui.Component.prototype.getElementsByClass = function(className) { + return this.element_ ? + this.dom_.getElementsByClass(className, this.element_) : + []; +}; + + +/** + * Returns the first element in this component's DOM with the provided + * className. + * @param {string} className The name of the class to look for. + * @return {Element} The first item with the class name provided. + */ +goog.ui.Component.prototype.getElementByClass = function(className) { + return this.element_ ? this.dom_.getElementByClass(className, this.element_) : + null; +}; + + +/** + * Similar to `getElementByClass` except that it expects the + * element to be present in the dom thus returning a required value. Otherwise, + * will assert. + * @param {string} className The name of the class to look for. + * @return {!Element} The first item with the class name provided. + */ +goog.ui.Component.prototype.getRequiredElementByClass = function(className) { + var el = this.getElementByClass(className); + goog.asserts.assert( + el, 'Expected element in component with class: %s', className); + return el; +}; + + +/** + * Returns the event handler for this component, lazily created the first time + * this method is called. + * @return {!goog.events.EventHandler} Event handler for this component. + * @protected + * @this {T} + * @template T + */ +goog.ui.Component.prototype.getHandler = function() { + // TODO(user): templated "this" values currently result in "this" being + // "unknown" in the body of the function. + var self = /** @type {goog.ui.Component} */ (this); + if (!self.googUiComponentHandler_) { + self.googUiComponentHandler_ = new goog.events.EventHandler(self); + } + return goog.asserts.assert(self.googUiComponentHandler_); +}; + + +/** + * Sets the parent of this component to use for event bubbling. Throws an error + * if the component already has a parent or if an attempt is made to add a + * component to itself as a child. Callers must use `removeChild` + * or `removeChildAt` to remove components from their containers before + * calling this method. + * @see goog.ui.Component#removeChild + * @see goog.ui.Component#removeChildAt + * @param {goog.ui.Component} parent The parent component. + */ +goog.ui.Component.prototype.setParent = function(parent) { + if (this == parent) { + // Attempting to add a child to itself is an error. + throw new Error(goog.ui.Component.Error.PARENT_UNABLE_TO_BE_SET); + } + + if (parent && this.parent_ && this.id_ && this.parent_.getChild(this.id_) && + this.parent_ != parent) { + // This component is already the child of some parent, so it should be + // removed using removeChild/removeChildAt first. + throw new Error(goog.ui.Component.Error.PARENT_UNABLE_TO_BE_SET); + } + + this.parent_ = parent; + goog.ui.Component.superClass_.setParentEventTarget.call(this, parent); +}; + + +/** + * Returns the component's parent, if any. + * @return {goog.ui.Component?} The parent component. + */ +goog.ui.Component.prototype.getParent = function() { + return this.parent_; +}; + + +/** + * Overrides {@link goog.events.EventTarget#setParentEventTarget} to throw an + * error if the parent component is set, and the argument is not the parent. + * @override + */ +goog.ui.Component.prototype.setParentEventTarget = function(parent) { + if (this.parent_ && this.parent_ != parent) { + throw new Error(goog.ui.Component.Error.NOT_SUPPORTED); + } + goog.ui.Component.superClass_.setParentEventTarget.call(this, parent); +}; + + +/** + * Returns the dom helper that is being used on this component. + * @return {!goog.dom.DomHelper} The dom helper used on this component. + */ +goog.ui.Component.prototype.getDomHelper = function() { + return this.dom_; +}; + + +/** + * Determines whether the component has been added to the document. + * @return {boolean} TRUE if rendered. Otherwise, FALSE. + */ +goog.ui.Component.prototype.isInDocument = function() { + return this.inDocument_; +}; + + +/** + * Creates the initial DOM representation for the component. The default + * implementation is to set this.element_ = div. + */ +goog.ui.Component.prototype.createDom = function() { + this.element_ = this.dom_.createElement(goog.dom.TagName.DIV); +}; + + +/** + * Renders the component. If a parent element is supplied, the component's + * element will be appended to it. If there is no optional parent element and + * the element doesn't have a parentNode then it will be appended to the + * document body. + * + * If this component has a parent component, and the parent component is + * not in the document already, then this will not call `enterDocument` + * on this component. + * + * Throws an Error if the component is already rendered. + * + * @param {Element=} opt_parentElement Optional parent element to render the + * component into. + */ +goog.ui.Component.prototype.render = function(opt_parentElement) { + this.render_(opt_parentElement); +}; + + +/** + * Renders the component before another element. The other element should be in + * the document already. + * + * Throws an Error if the component is already rendered. + * + * @param {Node} sibling Node to render the component before. + */ +goog.ui.Component.prototype.renderBefore = function(sibling) { + this.render_(/** @type {Element} */ (sibling.parentNode), sibling); +}; + + +/** + * Renders the component. If a parent element is supplied, the component's + * element will be appended to it. If there is no optional parent element and + * the element doesn't have a parentNode then it will be appended to the + * document body. + * + * If this component has a parent component, and the parent component is + * not in the document already, then this will not call `enterDocument` + * on this component. + * + * Throws an Error if the component is already rendered. + * + * @param {Element=} opt_parentElement Optional parent element to render the + * component into. + * @param {Node=} opt_beforeNode Node before which the component is to + * be rendered. If left out the node is appended to the parent element. + * @private + */ +goog.ui.Component.prototype.render_ = function( + opt_parentElement, opt_beforeNode) { + if (this.inDocument_) { + throw new Error(goog.ui.Component.Error.ALREADY_RENDERED); + } + + if (!this.element_) { + this.createDom(); + } + + if (opt_parentElement) { + opt_parentElement.insertBefore(this.element_, opt_beforeNode || null); + } else { + this.dom_.getDocument().body.appendChild(this.element_); + } + + // If this component has a parent component that isn't in the document yet, + // we don't call enterDocument() here. Instead, when the parent component + // enters the document, the enterDocument() call will propagate to its + // children, including this one. If the component doesn't have a parent + // or if the parent is already in the document, we call enterDocument(). + if (!this.parent_ || this.parent_.isInDocument()) { + this.enterDocument(); + } +}; + + +/** + * Decorates the element for the UI component. If the element is in the + * document, the enterDocument method will be called. + * + * If goog.ui.Component.ALLOW_DETACHED_DECORATION is false, the caller must + * pass an element that is in the document. + * + * @param {Element} element Element to decorate. + */ +goog.ui.Component.prototype.decorate = function(element) { + if (this.inDocument_) { + throw new Error(goog.ui.Component.Error.ALREADY_RENDERED); + } else if (element && this.canDecorate(element)) { + this.wasDecorated_ = true; + + // Set the DOM helper of the component to match the decorated element. + var doc = goog.dom.getOwnerDocument(element); + if (!this.dom_ || this.dom_.getDocument() != doc) { + this.dom_ = goog.dom.getDomHelper(element); + } + + // Call specific component decorate logic. + this.decorateInternal(element); + + // If supporting detached decoration, check that element is in doc. + if (!goog.ui.Component.ALLOW_DETACHED_DECORATION || + goog.dom.contains(doc, element)) { + this.enterDocument(); + } + } else { + throw new Error(goog.ui.Component.Error.DECORATE_INVALID); + } +}; + + +/** + * Determines if a given element can be decorated by this type of component. + * This method should be overridden by inheriting objects. + * @param {Element} element Element to decorate. + * @return {boolean} True if the element can be decorated, false otherwise. + */ +goog.ui.Component.prototype.canDecorate = function(element) { + return true; +}; + + +/** + * @return {boolean} Whether the component was decorated. + */ +goog.ui.Component.prototype.wasDecorated = function() { + return this.wasDecorated_; +}; + + +/** + * Actually decorates the element. Should be overridden by inheriting objects. + * This method can assume there are checks to ensure the component has not + * already been rendered have occurred and that enter document will be called + * afterwards. This method is considered protected. + * @param {Element} element Element to decorate. + * @protected + */ +goog.ui.Component.prototype.decorateInternal = function(element) { + this.element_ = element; +}; + + +/** + * Called when the component's element is known to be in the document. Anything + * using document.getElementById etc. should be done at this stage. + * + * If the component contains child components, this call is propagated to its + * children. + */ +goog.ui.Component.prototype.enterDocument = function() { + this.inDocument_ = true; + + // Propagate enterDocument to child components that have a DOM, if any. + // If a child was decorated before entering the document (permitted when + // goog.ui.Component.ALLOW_DETACHED_DECORATION is true), its enterDocument + // will be called here. + this.forEachChild(function(child) { + if (!child.isInDocument() && child.getElement()) { + child.enterDocument(); + } + }); +}; + + +/** + * Called by dispose to clean up the elements and listeners created by a + * component, or by a parent component/application who has removed the + * component from the document but wants to reuse it later. + * + * If the component contains child components, this call is propagated to its + * children. + * + * It should be possible for the component to be rendered again once this method + * has been called. + */ +goog.ui.Component.prototype.exitDocument = function() { + // Propagate exitDocument to child components that have been rendered, if any. + this.forEachChild(function(child) { + if (child.isInDocument()) { + child.exitDocument(); + } + }); + + if (this.googUiComponentHandler_) { + this.googUiComponentHandler_.removeAll(); + } + + this.inDocument_ = false; +}; + + +/** + * Disposes of the component. Calls `exitDocument`, which is expected to + * remove event handlers and clean up the component. Propagates the call to + * the component's children, if any. Removes the component's DOM from the + * document unless it was decorated. + * @override + * @protected + */ +goog.ui.Component.prototype.disposeInternal = function() { + if (this.inDocument_) { + this.exitDocument(); + } + + if (this.googUiComponentHandler_) { + this.googUiComponentHandler_.dispose(); + delete this.googUiComponentHandler_; + } + + // Disposes of the component's children, if any. + this.forEachChild(function(child) { child.dispose(); }); + + // Detach the component's element from the DOM, unless it was decorated. + if (!this.wasDecorated_ && this.element_) { + goog.dom.removeNode(this.element_); + } + + this.children_ = null; + this.childIndex_ = null; + this.element_ = null; + this.model_ = null; + this.parent_ = null; + + goog.ui.Component.superClass_.disposeInternal.call(this); +}; + + +/** + * Helper function for subclasses that gets a unique id for a given fragment, + * this can be used by components to generate unique string ids for DOM + * elements. + * @param {string} idFragment A partial id. + * @return {string} Unique element id. + */ +goog.ui.Component.prototype.makeId = function(idFragment) { + return this.getId() + '.' + idFragment; +}; + + +/** + * Makes a collection of ids. This is a convenience method for makeId. The + * object's values are the id fragments and the new values are the generated + * ids. The key will remain the same. + * @param {Object} object The object that will be used to create the ids. + * @return {!Object} An object of id keys to generated ids. + */ +goog.ui.Component.prototype.makeIds = function(object) { + var ids = {}; + for (var key in object) { + ids[key] = this.makeId(object[key]); + } + return ids; +}; + + +/** + * Returns the model associated with the UI component. + * @return {*} The model. + */ +goog.ui.Component.prototype.getModel = function() { + return this.model_; +}; + + +/** + * Sets the model associated with the UI component. + * @param {*} obj The model. + */ +goog.ui.Component.prototype.setModel = function(obj) { + this.model_ = obj; +}; + + +/** + * Helper function for returning the fragment portion of an id generated using + * makeId(). + * @param {string} id Id generated with makeId(). + * @return {string} Fragment. + */ +goog.ui.Component.prototype.getFragmentFromId = function(id) { + return id.substring(this.getId().length + 1); +}; + + +/** + * Helper function for returning an element in the document with a unique id + * generated using makeId(). + * @param {string} idFragment The partial id. + * @return {Element} The element with the unique id, or null if it cannot be + * found. + */ +goog.ui.Component.prototype.getElementByFragment = function(idFragment) { + if (!this.inDocument_) { + throw new Error(goog.ui.Component.Error.NOT_IN_DOCUMENT); + } + return this.dom_.getElement(this.makeId(idFragment)); +}; + + +/** + * Adds the specified component as the last child of this component. See + * {@link goog.ui.Component#addChildAt} for detailed semantics. + * + * @see goog.ui.Component#addChildAt + * @param {goog.ui.Component} child The new child component. + * @param {boolean=} opt_render If true, the child component will be rendered + * into the parent. + */ +goog.ui.Component.prototype.addChild = function(child, opt_render) { + // TODO(gboyer): addChildAt(child, this.getChildCount(), false) will + // reposition any already-rendered child to the end. Instead, perhaps + // addChild(child, false) should never reposition the child; instead, clients + // that need the repositioning will use addChildAt explicitly. Right now, + // clients can get around this by calling addChild before calling decorate. + this.addChildAt(child, this.getChildCount(), opt_render); +}; + + +/** + * Adds the specified component as a child of this component at the given + * 0-based index. + * + * Both `addChild` and `addChildAt` assume the following contract + * between parent and child components: + *
        + *
      • the child component's element must be a descendant of the parent + * component's element, and + *
      • the DOM state of the child component must be consistent with the DOM + * state of the parent component (see `isInDocument`) in the + * steady state -- the exception is to addChildAt(child, i, false) and + * then immediately decorate/render the child. + *
      + * + * In particular, `parent.addChild(child)` will throw an error if the + * child component is already in the document, but the parent isn't. + * + * Clients of this API may call `addChild` and `addChildAt` with + * `opt_render` set to true. If `opt_render` is true, calling these + * methods will automatically render the child component's element into the + * parent component's element. If the parent does not yet have an element, then + * `createDom` will automatically be invoked on the parent before + * rendering the child. + * + * Invoking {@code parent.addChild(child, true)} will throw an error if the + * child component is already in the document, regardless of the parent's DOM + * state. + * + * If `opt_render` is true and the parent component is not already + * in the document, `enterDocument` will not be called on this component + * at this point. + * + * Finally, this method also throws an error if the new child already has a + * different parent, or the given index is out of bounds. + * + * @see goog.ui.Component#addChild + * @param {goog.ui.Component} child The new child component. + * @param {number} index 0-based index at which the new child component is to be + * added; must be between 0 and the current child count (inclusive). + * @param {boolean=} opt_render If true, the child component will be rendered + * into the parent. + * @return {void} Nada. + */ +goog.ui.Component.prototype.addChildAt = function(child, index, opt_render) { + goog.asserts.assert(!!child, 'Provided element must not be null.'); + + if (child.inDocument_ && (opt_render || !this.inDocument_)) { + // Adding a child that's already in the document is an error, except if the + // parent is also in the document and opt_render is false (e.g. decorate()). + throw new Error(goog.ui.Component.Error.ALREADY_RENDERED); + } + + if (index < 0 || index > this.getChildCount()) { + // Allowing sparse child arrays would lead to strange behavior, so we don't. + throw new Error(goog.ui.Component.Error.CHILD_INDEX_OUT_OF_BOUNDS); + } + + // Create the index and the child array on first use. + if (!this.childIndex_ || !this.children_) { + this.childIndex_ = {}; + this.children_ = []; + } + + // Moving child within component, remove old reference. + if (child.getParent() == this) { + goog.object.set(this.childIndex_, child.getId(), child); + goog.array.remove(this.children_, child); + + // Add the child to this component. goog.object.add() throws an error if + // a child with the same ID already exists. + } else { + goog.object.add(this.childIndex_, child.getId(), child); + } + + // Set the parent of the child to this component. This throws an error if + // the child is already contained by another component. + child.setParent(this); + goog.array.insertAt(this.children_, child, index); + + if (child.inDocument_ && this.inDocument_ && child.getParent() == this) { + // Changing the position of an existing child, move the DOM node (if + // necessary). + var contentElement = this.getContentElement(); + var insertBeforeElement = contentElement.childNodes[index] || null; + if (insertBeforeElement != child.getElement()) { + contentElement.insertBefore(child.getElement(), insertBeforeElement); + } + } else if (opt_render) { + // If this (parent) component doesn't have a DOM yet, call createDom now + // to make sure we render the child component's element into the correct + // parent element (otherwise render_ with a null first argument would + // render the child into the document body, which is almost certainly not + // what we want). + if (!this.element_) { + this.createDom(); + } + // Render the child into the parent at the appropriate location. Note that + // getChildAt(index + 1) returns undefined if inserting at the end. + // TODO(attila): We should have a renderer with a renderChildAt API. + var sibling = this.getChildAt(index + 1); + // render_() calls enterDocument() if the parent is already in the document. + child.render_(this.getContentElement(), sibling ? sibling.element_ : null); + } else if ( + this.inDocument_ && !child.inDocument_ && child.element_ && + child.element_.parentNode && + // Under some circumstances, IE8 implicitly creates a Document Fragment + // for detached nodes, so ensure the parent is an Element as it should be. + child.element_.parentNode.nodeType == goog.dom.NodeType.ELEMENT) { + // We don't touch the DOM, but if the parent is in the document, and the + // child element is in the document but not marked as such, then we call + // enterDocument on the child. + // TODO(gboyer): It would be nice to move this condition entirely, but + // there's a large risk of breaking existing applications that manually + // append the child to the DOM and then call addChild. + child.enterDocument(); + } +}; + + +/** + * Returns the DOM element into which child components are to be rendered, + * or null if the component itself hasn't been rendered yet. This default + * implementation returns the component's root element. Subclasses with + * complex DOM structures must override this method. + * @return {Element} Element to contain child elements (null if none). + */ +goog.ui.Component.prototype.getContentElement = function() { + return this.element_; +}; + + +/** + * Returns true if the component is rendered right-to-left, false otherwise. + * The first time this function is invoked, the right-to-left rendering property + * is set if it has not been already. + * @return {boolean} Whether the control is rendered right-to-left. + */ +goog.ui.Component.prototype.isRightToLeft = function() { + if (this.rightToLeft_ == null) { + this.rightToLeft_ = goog.style.isRightToLeft( + this.inDocument_ ? this.element_ : this.dom_.getDocument().body); + } + return this.rightToLeft_; +}; + + +/** + * Set is right-to-left. This function should be used if the component needs + * to know the rendering direction during dom creation (i.e. before + * {@link #enterDocument} is called and is right-to-left is set). + * @param {boolean} rightToLeft Whether the component is rendered + * right-to-left. + */ +goog.ui.Component.prototype.setRightToLeft = function(rightToLeft) { + if (this.inDocument_) { + throw new Error(goog.ui.Component.Error.ALREADY_RENDERED); + } + this.rightToLeft_ = rightToLeft; +}; + + +/** + * Returns true if the component has children. + * @return {boolean} True if the component has children. + */ +goog.ui.Component.prototype.hasChildren = function() { + return !!this.children_ && this.children_.length != 0; +}; + + +/** + * Returns the number of children of this component. + * @return {number} The number of children. + */ +goog.ui.Component.prototype.getChildCount = function() { + return this.children_ ? this.children_.length : 0; +}; + + +/** + * Returns an array containing the IDs of the children of this component, or an + * empty array if the component has no children. + * @return {!Array} Child component IDs. + */ +goog.ui.Component.prototype.getChildIds = function() { + var ids = []; + + // We don't use goog.object.getKeys(this.childIndex_) because we want to + // return the IDs in the correct order as determined by this.children_. + this.forEachChild(function(child) { + // addChild()/addChildAt() guarantee that the child array isn't sparse. + ids.push(child.getId()); + }); + + return ids; +}; + + +/** + * Returns the child with the given ID, or null if no such child exists. + * @param {string} id Child component ID. + * @return {goog.ui.Component?} The child with the given ID; null if none. + */ +goog.ui.Component.prototype.getChild = function(id) { + // Use childIndex_ for O(1) access by ID. + return (this.childIndex_ && id) ? + /** @type {goog.ui.Component} */ ( + goog.object.get(this.childIndex_, id)) || + null : + null; +}; + + +/** + * Returns the child at the given index, or null if the index is out of bounds. + * @param {number} index 0-based index. + * @return {goog.ui.Component?} The child at the given index; null if none. + */ +goog.ui.Component.prototype.getChildAt = function(index) { + // Use children_ for access by index. + return this.children_ ? this.children_[index] || null : null; +}; + + +/** + * Calls the given function on each of this component's children in order. If + * `opt_obj` is provided, it will be used as the 'this' object in the + * function when called. The function should take two arguments: the child + * component and its 0-based index. The return value is ignored. + * @param {function(this:T,?,number):?} f The function to call for every + * child component; should take 2 arguments (the child and its index). + * @param {T=} opt_obj Used as the 'this' object in f when called. + * @template T + */ +goog.ui.Component.prototype.forEachChild = function(f, opt_obj) { + if (this.children_) { + goog.array.forEach(this.children_, f, opt_obj); + } +}; + + +/** + * Returns the 0-based index of the given child component, or -1 if no such + * child is found. + * @param {goog.ui.Component?} child The child component. + * @return {number} 0-based index of the child component; -1 if not found. + */ +goog.ui.Component.prototype.indexOfChild = function(child) { + return (this.children_ && child) ? goog.array.indexOf(this.children_, child) : + -1; +}; + + +/** + * Removes the given child from this component, and returns it. Throws an error + * if the argument is invalid or if the specified child isn't found in the + * parent component. The argument can either be a string (interpreted as the + * ID of the child component to remove) or the child component itself. + * + * If `opt_unrender` is true, calls {@link goog.ui.component#exitDocument} + * on the removed child, and subsequently detaches the child's DOM from the + * document. Otherwise it is the caller's responsibility to clean up the child + * component's DOM. + * + * @see goog.ui.Component#removeChildAt + * @param {string|goog.ui.Component|null} child The ID of the child to remove, + * or the child component itself. + * @param {boolean=} opt_unrender If true, calls `exitDocument` on the + * removed child component, and detaches its DOM from the document. + * @return {goog.ui.Component} The removed component, if any. + */ +goog.ui.Component.prototype.removeChild = function(child, opt_unrender) { + if (child) { + // Normalize child to be the object and id to be the ID string. This also + // ensures that the child is really ours. + var id = goog.isString(child) ? child : child.getId(); + child = this.getChild(id); + + if (id && child) { + goog.object.remove(this.childIndex_, id); + goog.array.remove(this.children_, child); + + if (opt_unrender) { + // Remove the child component's DOM from the document. We have to call + // exitDocument first (see documentation). + child.exitDocument(); + if (child.element_) { + goog.dom.removeNode(child.element_); + } + } + + // Child's parent must be set to null after exitDocument is called + // so that the child can unlisten to its parent if required. + child.setParent(null); + } + } + + if (!child) { + throw new Error(goog.ui.Component.Error.NOT_OUR_CHILD); + } + + return /** @type {!goog.ui.Component} */ (child); +}; + + +/** + * Removes the child at the given index from this component, and returns it. + * Throws an error if the argument is out of bounds, or if the specified child + * isn't found in the parent. See {@link goog.ui.Component#removeChild} for + * detailed semantics. + * + * @see goog.ui.Component#removeChild + * @param {number} index 0-based index of the child to remove. + * @param {boolean=} opt_unrender If true, calls `exitDocument` on the + * removed child component, and detaches its DOM from the document. + * @return {goog.ui.Component} The removed component, if any. + */ +goog.ui.Component.prototype.removeChildAt = function(index, opt_unrender) { + // removeChild(null) will throw error. + return this.removeChild(this.getChildAt(index), opt_unrender); +}; + + +/** + * Removes every child component attached to this one and returns them. + * + * @see goog.ui.Component#removeChild + * @param {boolean=} opt_unrender If true, calls {@link #exitDocument} on the + * removed child components, and detaches their DOM from the document. + * @return {!Array} The removed components if any. + */ +goog.ui.Component.prototype.removeChildren = function(opt_unrender) { + var removedChildren = []; + while (this.hasChildren()) { + removedChildren.push(this.removeChildAt(0, opt_unrender)); + } + return removedChildren; +}; + + +/** + * Returns whether this component should listen for PointerEvent types rather + * than MouseEvent types. This allows supporting drag gestures for touch/stylus + * input. + * @return {boolean} + */ +goog.ui.Component.prototype.pointerEventsEnabled = function() { + return this.pointerEventsEnabled_; +}; + + +/** + * Indicates whether this component should listen for PointerEvent types rather + * than MouseEvent types. This allows supporting drag gestures for touch/stylus + * input. Must be called before enterDocument to listen for the correct event + * types. + * @param {boolean} enable + */ +goog.ui.Component.prototype.setPointerEventsEnabled = function(enable) { + if (this.inDocument_) { + throw new Error(goog.ui.Component.Error.ALREADY_RENDERED); + } + this.pointerEventsEnabled_ = enable; +}; diff --git a/closure-library/closure/goog/ui/componentutil.js b/closure-library/closure/goog/ui/componentutil.js new file mode 100644 index 0000000000..0fc9fcd420 --- /dev/null +++ b/closure-library/closure/goog/ui/componentutil.js @@ -0,0 +1,36 @@ +// Copyright 2018 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 Static utility methods for UI components. + */ + +goog.provide('goog.ui.ComponentUtil'); + +goog.require('goog.events.MouseAsMouseEventType'); +goog.require('goog.events.MouseEvents'); +goog.require('goog.events.PointerAsMouseEventType'); + + + +/** + * @param {!goog.ui.Component} component + * @return {!goog.events.MouseEvents} The browser events that should be listened + * to for the given mouse events. + */ +goog.ui.ComponentUtil.getMouseEventType = function(component) { + return component.pointerEventsEnabled() ? + goog.events.PointerAsMouseEventType : + goog.events.MouseAsMouseEventType; +}; diff --git a/closure-library/closure/goog/ui/container.js b/closure-library/closure/goog/ui/container.js new file mode 100644 index 0000000000..02b1a58eac --- /dev/null +++ b/closure-library/closure/goog/ui/container.js @@ -0,0 +1,1402 @@ +// Copyright 2007 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 Base class for containers that host {@link goog.ui.Control}s, + * such as menus and toolbars. Provides default keyboard and mouse event + * handling and child management, based on a generalized version of + * {@link goog.ui.Menu}. + * + * @author attila@google.com (Attila Bodis) + * @see ../demos/container.html + */ +// TODO(attila): Fix code/logic duplication between this and goog.ui.Control. +// TODO(attila): Maybe pull common stuff all the way up into Component...? + +goog.provide('goog.ui.Container'); +goog.provide('goog.ui.Container.EventType'); +goog.provide('goog.ui.Container.Orientation'); + +goog.require('goog.a11y.aria'); +goog.require('goog.a11y.aria.State'); +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.events.EventType'); +goog.require('goog.events.KeyCodes'); +goog.require('goog.events.KeyHandler'); +goog.require('goog.object'); +goog.require('goog.style'); +goog.require('goog.ui.Component'); +goog.require('goog.ui.ComponentUtil'); +goog.require('goog.ui.ContainerRenderer'); +goog.require('goog.ui.Control'); + + + +/** + * Base class for containers. Extends {@link goog.ui.Component} by adding + * the following: + *
        + *
      • a {@link goog.events.KeyHandler}, to simplify keyboard handling, + *
      • a pluggable renderer framework, to simplify the creation of + * containers without the need to subclass this class, + *
      • methods to manage child controls hosted in the container, + *
      • default mouse and keyboard event handling methods. + *
      + * @param {?goog.ui.Container.Orientation=} opt_orientation Container + * orientation; defaults to `VERTICAL`. + * @param {goog.ui.ContainerRenderer=} opt_renderer Renderer used to render or + * decorate the container; defaults to {@link goog.ui.ContainerRenderer}. + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for document + * interaction. + * @extends {goog.ui.Component} + * @constructor + */ +goog.ui.Container = function(opt_orientation, opt_renderer, opt_domHelper) { + goog.ui.Component.call(this, opt_domHelper); + this.renderer_ = opt_renderer || goog.ui.ContainerRenderer.getInstance(); + this.orientation_ = opt_orientation || this.renderer_.getDefaultOrientation(); +}; +goog.inherits(goog.ui.Container, goog.ui.Component); +goog.tagUnsealableClass(goog.ui.Container); + + +/** + * Container-specific events. + * @enum {string} + */ +goog.ui.Container.EventType = { + /** + * Dispatched after a goog.ui.Container becomes visible. Non-cancellable. + * NOTE(user): This event really shouldn't exist, because the + * goog.ui.Component.EventType.SHOW event should behave like this one. But the + * SHOW event for containers has been behaving as other components' + * BEFORE_SHOW event for a long time, and too much code relies on that old + * behavior to fix it now. + */ + AFTER_SHOW: 'aftershow', + + /** + * Dispatched after a goog.ui.Container becomes invisible. Non-cancellable. + */ + AFTER_HIDE: 'afterhide' +}; + + +/** + * Container orientation constants. + * @enum {string} + */ +goog.ui.Container.Orientation = { + HORIZONTAL: 'horizontal', + VERTICAL: 'vertical' +}; + + +/** + * Allows an alternative element to be set to receive key events, otherwise + * defers to the renderer's element choice. + * @type {?Element|undefined} + * @private + */ +goog.ui.Container.prototype.keyEventTarget_ = null; + + +/** + * Keyboard event handler. + * @type {goog.events.KeyHandler?} + * @private + */ +goog.ui.Container.prototype.keyHandler_ = null; + + +/** + * Renderer for the container. Defaults to {@link goog.ui.ContainerRenderer}. + * @type {goog.ui.ContainerRenderer?} + * @private + */ +goog.ui.Container.prototype.renderer_ = null; + + +/** + * Container orientation; determines layout and default keyboard navigation. + * @type {?goog.ui.Container.Orientation} + * @private + */ +goog.ui.Container.prototype.orientation_ = null; + + +/** + * Whether the container is set to be visible. Defaults to true. + * @type {boolean} + * @private + */ +goog.ui.Container.prototype.visible_ = true; + + +/** + * Whether the container is enabled and reacting to keyboard and mouse events. + * Defaults to true. + * @type {boolean} + * @private + */ +goog.ui.Container.prototype.enabled_ = true; + + +/** + * Whether the container supports keyboard focus. Defaults to true. Focusable + * containers have a `tabIndex` and can be navigated to via the keyboard. + * @type {boolean} + * @private + */ +goog.ui.Container.prototype.focusable_ = true; + + +/** + * The 0-based index of the currently highlighted control in the container + * (-1 if none). + * @type {number} + * @private + */ +goog.ui.Container.prototype.highlightedIndex_ = -1; + + +/** + * The currently open (expanded) control in the container (null if none). + * @type {goog.ui.Control?} + * @private + */ +goog.ui.Container.prototype.openItem_ = null; + + +/** + * Whether the mouse button is held down. Defaults to false. This flag is set + * when the user mouses down over the container, and remains set until they + * release the mouse button. + * @type {boolean} + * @private + */ +goog.ui.Container.prototype.mouseButtonPressed_ = false; + + +/** + * Whether focus of child components should be allowed. Only effective if + * focusable_ is set to false. + * @type {boolean} + * @private + */ +goog.ui.Container.prototype.allowFocusableChildren_ = false; + + +/** + * Whether highlighting a child component should also open it. + * @type {boolean} + * @private + */ +goog.ui.Container.prototype.openFollowsHighlight_ = true; + + +/** + * Map of DOM IDs to child controls. Each key is the DOM ID of a child + * control's root element; each value is a reference to the child control + * itself. Used for looking up the child control corresponding to a DOM + * node in O(1) time. + * @type {?Object} + * @private + */ +goog.ui.Container.prototype.childElementIdMap_ = null; + + +// Event handler and renderer management. + + +/** + * Returns the DOM element on which the container is listening for keyboard + * events (null if none). + * @return {Element} Element on which the container is listening for key + * events. + */ +goog.ui.Container.prototype.getKeyEventTarget = function() { + // Delegate to renderer, unless we've set an explicit target. + return this.keyEventTarget_ || this.renderer_.getKeyEventTarget(this); +}; + + +/** + * Attaches an element on which to listen for key events. + * @param {Element|undefined} element The element to attach, or null/undefined + * to attach to the default element. + */ +goog.ui.Container.prototype.setKeyEventTarget = function(element) { + if (this.focusable_) { + var oldTarget = this.getKeyEventTarget(); + var inDocument = this.isInDocument(); + + this.keyEventTarget_ = element; + var newTarget = this.getKeyEventTarget(); + + if (inDocument) { + // Unlisten for events on the old key target. Requires us to reset + // key target state temporarily. + this.keyEventTarget_ = oldTarget; + this.enableFocusHandling_(false); + this.keyEventTarget_ = element; + + // Listen for events on the new key target. + this.getKeyHandler().attach(newTarget); + this.enableFocusHandling_(true); + } + } else { + throw new Error( + 'Can\'t set key event target for container ' + + 'that doesn\'t support keyboard focus!'); + } +}; + + +/** + * Returns the keyboard event handler for this container, lazily created the + * first time this method is called. The keyboard event handler listens for + * keyboard events on the container's key event target, as determined by its + * renderer. + * @return {!goog.events.KeyHandler} Keyboard event handler for this container. + */ +goog.ui.Container.prototype.getKeyHandler = function() { + return this.keyHandler_ || + (this.keyHandler_ = new goog.events.KeyHandler(this.getKeyEventTarget())); +}; + + +/** + * Returns the renderer used by this container to render itself or to decorate + * an existing element. + * @return {goog.ui.ContainerRenderer} Renderer used by the container. + */ +goog.ui.Container.prototype.getRenderer = function() { + return this.renderer_; +}; + + +/** + * Registers the given renderer with the container. Changing renderers after + * the container has already been rendered or decorated is an error. + * @param {goog.ui.ContainerRenderer} renderer Renderer used by the container. + */ +goog.ui.Container.prototype.setRenderer = function(renderer) { + if (this.getElement()) { + // Too late. + throw new Error(goog.ui.Component.Error.ALREADY_RENDERED); + } + + this.renderer_ = renderer; +}; + + +// Standard goog.ui.Component implementation. + + +/** + * Creates the container's DOM. + * @override + */ +goog.ui.Container.prototype.createDom = function() { + // Delegate to renderer. + this.setElementInternal(this.renderer_.createDom(this)); +}; + + +/** + * Returns the DOM element into which child components are to be rendered, + * or null if the container itself hasn't been rendered yet. Overrides + * {@link goog.ui.Component#getContentElement} by delegating to the renderer. + * @return {Element} Element to contain child elements (null if none). + * @override + */ +goog.ui.Container.prototype.getContentElement = function() { + // Delegate to renderer. + return this.renderer_.getContentElement(this.getElement()); +}; + + +/** + * Returns true if the given element can be decorated by this container. + * Overrides {@link goog.ui.Component#canDecorate}. + * @param {Element} element Element to decorate. + * @return {boolean} True iff the element can be decorated. + * @override + */ +goog.ui.Container.prototype.canDecorate = function(element) { + // Delegate to renderer. + return this.renderer_.canDecorate(element); +}; + + +/** + * Decorates the given element with this container. Overrides {@link + * goog.ui.Component#decorateInternal}. Considered protected. + * @param {Element} element Element to decorate. + * @override + */ +goog.ui.Container.prototype.decorateInternal = function(element) { + // Delegate to renderer. + this.setElementInternal(this.renderer_.decorate(this, element)); + // Check whether the decorated element is explicitly styled to be invisible. + if (element.style.display == 'none') { + this.visible_ = false; + } +}; + + +/** + * Configures the container after its DOM has been rendered, and sets up event + * handling. Overrides {@link goog.ui.Component#enterDocument}. + * @override + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.Container.prototype.enterDocument = function() { + goog.ui.Container.superClass_.enterDocument.call(this); + + this.forEachChild(function(child) { + if (child.isInDocument()) { + this.registerChildId_(child); + } + }, this); + + var elem = this.getElement(); + + // Call the renderer's initializeDom method to initialize the container's DOM. + this.renderer_.initializeDom(this); + + // Initialize visibility (opt_force = true, so we don't dispatch events). + this.setVisible(this.visible_, true); + + var MouseEventType = goog.ui.ComponentUtil.getMouseEventType(this); + + // Handle events dispatched by child controls. + this.getHandler() + .listen(this, goog.ui.Component.EventType.ENTER, this.handleEnterItem) + .listen( + this, goog.ui.Component.EventType.HIGHLIGHT, this.handleHighlightItem) + .listen( + this, goog.ui.Component.EventType.UNHIGHLIGHT, + this.handleUnHighlightItem) + .listen(this, goog.ui.Component.EventType.OPEN, this.handleOpenItem) + .listen(this, goog.ui.Component.EventType.CLOSE, this.handleCloseItem) + + // Handle mouse events. + .listen(elem, MouseEventType.MOUSEDOWN, this.handleMouseDown) + .listen( + goog.dom.getOwnerDocument(elem), + [MouseEventType.MOUSEUP, MouseEventType.MOUSECANCEL], + this.handleDocumentMouseUp) + + // Handle mouse events on behalf of controls in the container. + .listen( + elem, + [ + MouseEventType.MOUSEDOWN, MouseEventType.MOUSEUP, + MouseEventType.MOUSECANCEL, goog.events.EventType.MOUSEOVER, + goog.events.EventType.MOUSEOUT, goog.events.EventType.CONTEXTMENU + ], + this.handleChildMouseEvents); + + if (this.pointerEventsEnabled()) { + // Prevent pointer events from capturing the target element so they behave + // more like mouse events. + this.getHandler().listen( + elem, goog.events.EventType.GOTPOINTERCAPTURE, + this.preventPointerCapture_); + } + + // If the container is focusable, set up keyboard event handling. + if (this.isFocusable()) { + this.enableFocusHandling_(true); + } +}; + + +/** + * @param {!goog.events.BrowserEvent} e Event to handle. + * @private + */ +goog.ui.Container.prototype.preventPointerCapture_ = function(e) { + var elem = /** @type {!Element} */ (e.target); + if (!!elem.releasePointerCapture) { + elem.releasePointerCapture(e.pointerId); + } +}; + + +/** + * Sets up listening for events applicable to focusable containers. + * @param {boolean} enable Whether to enable or disable focus handling. + * @private + */ +goog.ui.Container.prototype.enableFocusHandling_ = function(enable) { + var handler = this.getHandler(); + var keyTarget = this.getKeyEventTarget(); + if (enable) { + handler.listen(keyTarget, goog.events.EventType.FOCUS, this.handleFocus) + .listen(keyTarget, goog.events.EventType.BLUR, this.handleBlur) + .listen( + this.getKeyHandler(), goog.events.KeyHandler.EventType.KEY, + this.handleKeyEvent); + } else { + handler.unlisten(keyTarget, goog.events.EventType.FOCUS, this.handleFocus) + .unlisten(keyTarget, goog.events.EventType.BLUR, this.handleBlur) + .unlisten( + this.getKeyHandler(), goog.events.KeyHandler.EventType.KEY, + this.handleKeyEvent); + } +}; + + +/** + * Cleans up the container before its DOM is removed from the document, and + * removes event handlers. Overrides {@link goog.ui.Component#exitDocument}. + * @override + */ +goog.ui.Container.prototype.exitDocument = function() { + // {@link #setHighlightedIndex} has to be called before + // {@link goog.ui.Component#exitDocument}, otherwise it has no effect. + this.setHighlightedIndex(-1); + + if (this.openItem_) { + this.openItem_.setOpen(false); + } + + this.mouseButtonPressed_ = false; + + goog.ui.Container.superClass_.exitDocument.call(this); +}; + + +/** @override */ +goog.ui.Container.prototype.disposeInternal = function() { + goog.ui.Container.superClass_.disposeInternal.call(this); + + if (this.keyHandler_) { + this.keyHandler_.dispose(); + this.keyHandler_ = null; + } + + this.keyEventTarget_ = null; + this.childElementIdMap_ = null; + this.openItem_ = null; + this.renderer_ = null; +}; + + +// Default event handlers. + + +/** + * Handles ENTER events raised by child controls when they are navigated to. + * @param {goog.events.Event} e ENTER event to handle. + * @return {boolean} Whether to prevent handleMouseOver from handling + * the event. + */ +goog.ui.Container.prototype.handleEnterItem = function(e) { + // Allow the Control to highlight itself. + return true; +}; + + +/** + * Handles HIGHLIGHT events dispatched by items in the container when + * they are highlighted. + * @param {goog.events.Event} e Highlight event to handle. + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.Container.prototype.handleHighlightItem = function(e) { + var index = this.indexOfChild(/** @type {goog.ui.Control} */ (e.target)); + if (index > -1 && index != this.highlightedIndex_) { + var item = this.getHighlighted(); + if (item) { + // Un-highlight previously highlighted item. + item.setHighlighted(false); + } + + this.highlightedIndex_ = index; + item = this.getHighlighted(); + + if (this.isMouseButtonPressed()) { + // Activate item when mouse button is pressed, to allow MacOS-style + // dragging to choose menu items. Although this should only truly + // happen if the highlight is due to mouse movements, there is little + // harm in doing it for keyboard or programmatic highlights. + item.setActive(true); + } + + // Update open item if open item needs follow highlight. + if (this.openFollowsHighlight_ && this.openItem_ && + item != this.openItem_) { + if (item.isSupportedState(goog.ui.Component.State.OPENED)) { + item.setOpen(true); + } else { + this.openItem_.setOpen(false); + } + } + } + + var element = this.getElement(); + goog.asserts.assert( + element, 'The DOM element for the container cannot be null.'); + if (e.target.getElement() != null) { + goog.a11y.aria.setState( + element, goog.a11y.aria.State.ACTIVEDESCENDANT, + e.target.getElement().id); + } +}; + + +/** + * Handles UNHIGHLIGHT events dispatched by items in the container when + * they are unhighlighted. + * @param {goog.events.Event} e Unhighlight event to handle. + */ +goog.ui.Container.prototype.handleUnHighlightItem = function(e) { + if (e.target == this.getHighlighted()) { + this.highlightedIndex_ = -1; + } + var element = this.getElement(); + goog.asserts.assert( + element, 'The DOM element for the container cannot be null.'); + // Setting certain ARIA attributes to empty strings is problematic. + // Just remove the attribute instead. + goog.a11y.aria.removeState(element, goog.a11y.aria.State.ACTIVEDESCENDANT); +}; + + +/** + * Handles OPEN events dispatched by items in the container when they are + * opened. + * @param {goog.events.Event} e Open event to handle. + */ +goog.ui.Container.prototype.handleOpenItem = function(e) { + var item = /** @type {goog.ui.Control} */ (e.target); + if (item && item != this.openItem_ && item.getParent() == this) { + if (this.openItem_) { + this.openItem_.setOpen(false); + } + this.openItem_ = item; + } +}; + + +/** + * Handles CLOSE events dispatched by items in the container when they are + * closed. + * @param {goog.events.Event} e Close event to handle. + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.Container.prototype.handleCloseItem = function(e) { + if (e.target == this.openItem_) { + this.openItem_ = null; + } + + var element = this.getElement(); + var targetEl = e.target.getElement(); + // Set the active descendant to the menu item when its submenu is closed and + // it is still highlighted. This can sometimes be called when the menuitem is + // unhighlighted because the focus moved elsewhere, do nothing at that point. + if (element && e.target.isHighlighted() && targetEl) { + goog.a11y.aria.setActiveDescendant(element, targetEl); + } +}; + + +/** + * Handles mousedown events over the container. The default implementation + * sets the "mouse button pressed" flag and, if the container is focusable, + * grabs keyboard focus. + * @param {goog.events.BrowserEvent} e Mousedown event to handle. + */ +goog.ui.Container.prototype.handleMouseDown = function(e) { + if (this.enabled_) { + this.setMouseButtonPressed(true); + } + + var keyTarget = this.getKeyEventTarget(); + if (keyTarget && goog.dom.isFocusableTabIndex(keyTarget)) { + // The container is configured to receive keyboard focus. + keyTarget.focus(); + } else { + // The control isn't configured to receive keyboard focus; prevent it + // from stealing focus or destroying the selection. + e.preventDefault(); + } +}; + + +/** + * Handles mouseup events over the document. The default implementation + * clears the "mouse button pressed" flag. + * @param {goog.events.BrowserEvent} e Mouseup event to handle. + */ +goog.ui.Container.prototype.handleDocumentMouseUp = function(e) { + this.setMouseButtonPressed(false); +}; + + +/** + * Handles mouse events originating from nodes belonging to the controls hosted + * in the container. Locates the child control based on the DOM node that + * dispatched the event, and forwards the event to the control for handling. + * @param {goog.events.BrowserEvent} e Mouse event to handle. + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.Container.prototype.handleChildMouseEvents = function(e) { + var MouseEventType = goog.ui.ComponentUtil.getMouseEventType(this); + + var control = this.getOwnerControl(/** @type {Node} */ (e.target)); + if (control) { + // Child control identified; forward the event. + switch (e.type) { + case MouseEventType.MOUSEDOWN: + control.handleMouseDown(e); + break; + case MouseEventType.MOUSEUP: + case MouseEventType.MOUSECANCEL: + control.handleMouseUp(e); + break; + case goog.events.EventType.MOUSEOVER: + control.handleMouseOver(e); + break; + case goog.events.EventType.MOUSEOUT: + control.handleMouseOut(e); + break; + case goog.events.EventType.CONTEXTMENU: + control.handleContextMenu(e); + break; + } + } +}; + + +/** + * Returns the child control that owns the given DOM node, or null if no such + * control is found. + * @param {Node} node DOM node whose owner is to be returned. + * @return {goog.ui.Control?} Control hosted in the container to which the node + * belongs (if found). + * @protected + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.Container.prototype.getOwnerControl = function(node) { + // Ensure that this container actually has child controls before + // looking up the owner. + if (this.childElementIdMap_) { + var elem = this.getElement(); + // See http://b/2964418 . IE9 appears to evaluate '!=' incorrectly, so + // using '!==' instead. + // TODO(user): Possibly revert this change if/when IE9 fixes the issue. + while (node && node !== elem) { + var id = node.id; + if (id in this.childElementIdMap_) { + return this.childElementIdMap_[id]; + } + node = node.parentNode; + } + } + return null; +}; + + +/** + * Handles focus events raised when the container's key event target receives + * keyboard focus. + * @param {goog.events.BrowserEvent} e Focus event to handle. + */ +goog.ui.Container.prototype.handleFocus = function(e) { + // No-op in the base class. +}; + + +/** + * Handles blur events raised when the container's key event target loses + * keyboard focus. The default implementation clears the highlight index. + * @param {goog.events.BrowserEvent} e Blur event to handle. + */ +goog.ui.Container.prototype.handleBlur = function(e) { + this.setHighlightedIndex(-1); + this.setMouseButtonPressed(false); + // If the container loses focus, and one of its children is open, close it. + if (this.openItem_) { + this.openItem_.setOpen(false); + } +}; + + +/** + * Attempts to handle a keyboard event, if the control is enabled, by calling + * {@link handleKeyEventInternal}. Considered protected; should only be used + * within this package and by subclasses. + * @param {goog.events.KeyEvent} e Key event to handle. + * @return {boolean} Whether the key event was handled. + */ +goog.ui.Container.prototype.handleKeyEvent = function(e) { + if (this.isEnabled() && this.isVisible() && + (this.getChildCount() != 0 || this.keyEventTarget_) && + this.handleKeyEventInternal(e)) { + e.preventDefault(); + e.stopPropagation(); + return true; + } + return false; +}; + + +/** + * Attempts to handle a keyboard event; returns true if the event was handled, + * false otherwise. If the container is enabled, and a child is highlighted, + * calls the child control's `handleKeyEvent` method to give the control + * a chance to handle the event first. + * @param {goog.events.KeyEvent} e Key event to handle. + * @return {boolean} Whether the event was handled by the container (or one of + * its children). + */ +goog.ui.Container.prototype.handleKeyEventInternal = function(e) { + // Give the highlighted control the chance to handle the key event. + var highlighted = this.getHighlighted(); + if (highlighted && typeof highlighted.handleKeyEvent == 'function' && + highlighted.handleKeyEvent(e)) { + return true; + } + + // Give the open control the chance to handle the key event. + if (this.openItem_ && this.openItem_ != highlighted && + typeof this.openItem_.handleKeyEvent == 'function' && + this.openItem_.handleKeyEvent(e)) { + return true; + } + + // Do not handle the key event if any modifier key is pressed. + if (e.shiftKey || e.ctrlKey || e.metaKey || e.altKey) { + return false; + } + + // Either nothing is highlighted, or the highlighted control didn't handle + // the key event, so attempt to handle it here. + switch (e.keyCode) { + case goog.events.KeyCodes.ESC: + if (this.isFocusable()) { + this.getKeyEventTarget().blur(); + } else { + return false; + } + break; + + case goog.events.KeyCodes.HOME: + this.highlightFirst(); + break; + + case goog.events.KeyCodes.END: + this.highlightLast(); + break; + + case goog.events.KeyCodes.UP: + if (this.orientation_ == goog.ui.Container.Orientation.VERTICAL) { + this.highlightPrevious(); + } else { + return false; + } + break; + + case goog.events.KeyCodes.LEFT: + if (this.orientation_ == goog.ui.Container.Orientation.HORIZONTAL) { + if (this.isRightToLeft()) { + this.highlightNext(); + } else { + this.highlightPrevious(); + } + } else { + return false; + } + break; + + case goog.events.KeyCodes.DOWN: + if (this.orientation_ == goog.ui.Container.Orientation.VERTICAL) { + this.highlightNext(); + } else { + return false; + } + break; + + case goog.events.KeyCodes.RIGHT: + if (this.orientation_ == goog.ui.Container.Orientation.HORIZONTAL) { + if (this.isRightToLeft()) { + this.highlightPrevious(); + } else { + this.highlightNext(); + } + } else { + return false; + } + break; + + default: + return false; + } + + return true; +}; + + +// Child component management. + + +/** + * Creates a DOM ID for the child control and registers it to an internal + * hash table to be able to find it fast by id. + * @param {goog.ui.Component} child The child control. Its root element has + * to be created yet. + * @private + */ +goog.ui.Container.prototype.registerChildId_ = function(child) { + // Map the DOM ID of the control's root element to the control itself. + var childElem = child.getElement(); + + // If the control's root element doesn't have a DOM ID assign one. + var id = childElem.id || (childElem.id = child.getId()); + + // Lazily create the child element ID map on first use. + if (!this.childElementIdMap_) { + this.childElementIdMap_ = {}; + } + this.childElementIdMap_[id] = child; +}; + + +/** + * Adds the specified control as the last child of this container. See + * {@link goog.ui.Container#addChildAt} for detailed semantics. + * @param {goog.ui.Component} child The new child control. + * @param {boolean=} opt_render Whether the new child should be rendered + * immediately after being added (defaults to false). + * @override + */ +goog.ui.Container.prototype.addChild = function(child, opt_render) { + goog.asserts.assertInstanceof( + child, goog.ui.Control, 'The child of a container must be a control'); + goog.ui.Container.superClass_.addChild.call(this, child, opt_render); +}; + + +/** + * Overrides {@link goog.ui.Container#getChild} to make it clear that it + * only returns {@link goog.ui.Control}s. + * @param {string} id Child component ID. + * @return {goog.ui.Control} The child with the given ID; null if none. + * @override + */ +goog.ui.Container.prototype.getChild; + + +/** + * Overrides {@link goog.ui.Container#getChildAt} to make it clear that it + * only returns {@link goog.ui.Control}s. + * @param {number} index 0-based index. + * @return {goog.ui.Control} The child with the given ID; null if none. + * @override + */ +goog.ui.Container.prototype.getChildAt; + + +/** + * Adds the control as a child of this container at the given 0-based index. + * Overrides {@link goog.ui.Component#addChildAt} by also updating the + * container's highlight index. Since {@link goog.ui.Component#addChild} uses + * {@link #addChildAt} internally, we only need to override this method. + * @param {goog.ui.Component} control New child. + * @param {number} index Index at which the new child is to be added. + * @param {boolean=} opt_render Whether the new child should be rendered + * immediately after being added (defaults to false). + * @override + */ +goog.ui.Container.prototype.addChildAt = function(control, index, opt_render) { + goog.asserts.assertInstanceof(control, goog.ui.Control); + + // Make sure the child control dispatches HIGHLIGHT, UNHIGHLIGHT, OPEN, and + // CLOSE events, and that it doesn't steal keyboard focus. + control.setDispatchTransitionEvents(goog.ui.Component.State.HOVER, true); + control.setDispatchTransitionEvents(goog.ui.Component.State.OPENED, true); + if (this.isFocusable() || !this.isFocusableChildrenAllowed()) { + control.setSupportedState(goog.ui.Component.State.FOCUSED, false); + } + + // Disable mouse event handling by child controls. + control.setHandleMouseEvents(false); + + var srcIndex = + (control.getParent() == this) ? this.indexOfChild(control) : -1; + + // Let the superclass implementation do the work. + goog.ui.Container.superClass_.addChildAt.call( + this, control, index, opt_render); + + if (control.isInDocument() && this.isInDocument()) { + this.registerChildId_(control); + } + + this.updateHighlightedIndex_(srcIndex, index); +}; + + +/** + * Updates the highlighted index when children are added or moved. + * @param {number} fromIndex Index of the child before it was moved, or -1 if + * the child was added. + * @param {number} toIndex Index of the child after it was moved or added. + * @private + */ +goog.ui.Container.prototype.updateHighlightedIndex_ = function( + fromIndex, toIndex) { + if (fromIndex == -1) { + fromIndex = this.getChildCount(); + } + if (fromIndex == this.highlightedIndex_) { + // The highlighted element itself was moved. + this.highlightedIndex_ = Math.min(this.getChildCount() - 1, toIndex); + } else if ( + fromIndex > this.highlightedIndex_ && toIndex <= this.highlightedIndex_) { + // The control was added or moved behind the highlighted index. + this.highlightedIndex_++; + } else if ( + fromIndex < this.highlightedIndex_ && toIndex > this.highlightedIndex_) { + // The control was moved from before to behind the highlighted index. + this.highlightedIndex_--; + } +}; + + +/** + * Removes a child control. Overrides {@link goog.ui.Component#removeChild} by + * updating the highlight index. Since {@link goog.ui.Component#removeChildAt} + * uses {@link #removeChild} internally, we only need to override this method. + * @param {string|goog.ui.Component} control The ID of the child to remove, or + * the control itself. + * @param {boolean=} opt_unrender Whether to call `exitDocument` on the + * removed control, and detach its DOM from the document (defaults to + * false). + * @return {goog.ui.Control} The removed control, if any. + * @override + */ +goog.ui.Container.prototype.removeChild = function(control, opt_unrender) { + control = goog.isString(control) ? this.getChild(control) : control; + goog.asserts.assertInstanceof(control, goog.ui.Control); + + if (control) { + var index = this.indexOfChild(control); + if (index != -1) { + if (index == this.highlightedIndex_) { + control.setHighlighted(false); + this.highlightedIndex_ = -1; + } else if (index < this.highlightedIndex_) { + this.highlightedIndex_--; + } + } + + // Remove the mapping from the child element ID map. + var childElem = control.getElement(); + if (childElem && childElem.id && this.childElementIdMap_) { + goog.object.remove(this.childElementIdMap_, childElem.id); + } + } + + control = /** @type {!goog.ui.Control} */ ( + goog.ui.Container.superClass_.removeChild.call( + this, control, opt_unrender)); + + // Re-enable mouse event handling (in case the control is reused elsewhere). + control.setHandleMouseEvents(true); + + return control; +}; + + +// Container state management. + + +/** + * Returns the container's orientation. + * @return {?goog.ui.Container.Orientation} Container orientation. + */ +goog.ui.Container.prototype.getOrientation = function() { + return this.orientation_; +}; + + +/** + * Sets the container's orientation. + * @param {goog.ui.Container.Orientation} orientation Container orientation. + */ +// TODO(attila): Do we need to support containers with dynamic orientation? +goog.ui.Container.prototype.setOrientation = function(orientation) { + if (this.getElement()) { + // Too late. + throw new Error(goog.ui.Component.Error.ALREADY_RENDERED); + } + + this.orientation_ = orientation; +}; + + +/** + * Returns true if the container's visibility is set to visible, false if + * it is set to hidden. A container that is set to hidden is guaranteed + * to be hidden from the user, but the reverse isn't necessarily true. + * A container may be set to visible but can otherwise be obscured by another + * element, rendered off-screen, or hidden using direct CSS manipulation. + * @return {boolean} Whether the container is set to be visible. + */ +goog.ui.Container.prototype.isVisible = function() { + return this.visible_; +}; + + +/** + * Shows or hides the container. Does nothing if the container already has + * the requested visibility. Otherwise, dispatches a SHOW or HIDE event as + * appropriate, giving listeners a chance to prevent the visibility change. + * @param {boolean} visible Whether to show or hide the container. + * @param {boolean=} opt_force If true, doesn't check whether the container + * already has the requested visibility, and doesn't dispatch any events. + * @return {boolean} Whether the visibility was changed. + */ +goog.ui.Container.prototype.setVisible = function(visible, opt_force) { + if (opt_force || (this.visible_ != visible && + this.dispatchEvent( + visible ? goog.ui.Component.EventType.SHOW : + goog.ui.Component.EventType.HIDE))) { + this.visible_ = visible; + + var elem = this.getElement(); + if (elem) { + goog.style.setElementShown(elem, visible); + if (this.isFocusable()) { + // Enable keyboard access only for enabled & visible containers. + this.renderer_.enableTabIndex( + this.getKeyEventTarget(), this.enabled_ && this.visible_); + } + if (!opt_force) { + this.dispatchEvent( + this.visible_ ? goog.ui.Container.EventType.AFTER_SHOW : + goog.ui.Container.EventType.AFTER_HIDE); + } + } + + return true; + } + + return false; +}; + + +/** + * Returns true if the container is enabled, false otherwise. + * @return {boolean} Whether the container is enabled. + */ +goog.ui.Container.prototype.isEnabled = function() { + return this.enabled_; +}; + + +/** + * Enables/disables the container based on the `enable` argument. + * Dispatches an `ENABLED` or `DISABLED` event prior to changing + * the container's state, which may be caught and canceled to prevent the + * container from changing state. Also enables/disables child controls. + * @param {boolean} enable Whether to enable or disable the container. + */ +goog.ui.Container.prototype.setEnabled = function(enable) { + if (this.enabled_ != enable && + this.dispatchEvent( + enable ? goog.ui.Component.EventType.ENABLE : + goog.ui.Component.EventType.DISABLE)) { + if (enable) { + // Flag the container as enabled first, then update children. This is + // because controls can't be enabled if their parent is disabled. + this.enabled_ = true; + this.forEachChild(function(child) { + // Enable child control unless it is flagged. + if (child.wasDisabled) { + delete child.wasDisabled; + } else { + child.setEnabled(true); + } + }); + } else { + // Disable children first, then flag the container as disabled. This is + // because controls can't be disabled if their parent is already disabled. + this.forEachChild(function(child) { + // Disable child control, or flag it if it's already disabled. + if (child.isEnabled()) { + child.setEnabled(false); + } else { + child.wasDisabled = true; + } + }); + this.enabled_ = false; + this.setMouseButtonPressed(false); + } + + if (this.isFocusable()) { + // Enable keyboard access only for enabled & visible components. + this.renderer_.enableTabIndex( + this.getKeyEventTarget(), enable && this.visible_); + } + } +}; + + +/** + * Returns true if the container is focusable, false otherwise. The default + * is true. Focusable containers always have a tab index and allocate a key + * handler to handle keyboard events while focused. + * @return {boolean} Whether the component is focusable. + */ +goog.ui.Container.prototype.isFocusable = function() { + return this.focusable_; +}; + + +/** + * Sets whether the container is focusable. The default is true. Focusable + * containers always have a tab index and allocate a key handler to handle + * keyboard events while focused. + * @param {boolean} focusable Whether the component is to be focusable. + */ +goog.ui.Container.prototype.setFocusable = function(focusable) { + if (focusable != this.focusable_ && this.isInDocument()) { + this.enableFocusHandling_(focusable); + } + this.focusable_ = focusable; + if (this.enabled_ && this.visible_) { + this.renderer_.enableTabIndex(this.getKeyEventTarget(), focusable); + } +}; + + +/** + * Returns true if the container allows children to be focusable, false + * otherwise. Only effective if the container is not focusable. + * @return {boolean} Whether children should be focusable. + */ +goog.ui.Container.prototype.isFocusableChildrenAllowed = function() { + return this.allowFocusableChildren_; +}; + + +/** + * Sets whether the container allows children to be focusable, false + * otherwise. Only effective if the container is not focusable. + * @param {boolean} focusable Whether the children should be focusable. + */ +goog.ui.Container.prototype.setFocusableChildrenAllowed = function(focusable) { + this.allowFocusableChildren_ = focusable; +}; + + +/** + * @return {boolean} Whether highlighting a child component should also open it. + */ +goog.ui.Container.prototype.isOpenFollowsHighlight = function() { + return this.openFollowsHighlight_; +}; + + +/** + * Sets whether highlighting a child component should also open it. + * @param {boolean} follow Whether highlighting a child component also opens it. + */ +goog.ui.Container.prototype.setOpenFollowsHighlight = function(follow) { + this.openFollowsHighlight_ = follow; +}; + + +// Highlight management. + + +/** + * Returns the index of the currently highlighted item (-1 if none). + * @return {number} Index of the currently highlighted item. + */ +goog.ui.Container.prototype.getHighlightedIndex = function() { + return this.highlightedIndex_; +}; + + +/** + * Highlights the item at the given 0-based index (if any). If another item + * was previously highlighted, it is un-highlighted. + * @param {number} index Index of item to highlight (-1 removes the current + * highlight). + */ +goog.ui.Container.prototype.setHighlightedIndex = function(index) { + var child = this.getChildAt(index); + if (child) { + child.setHighlighted(true); + } else if (this.highlightedIndex_ > -1) { + this.getHighlighted().setHighlighted(false); + } +}; + + +/** + * Highlights the given item if it exists and is a child of the container; + * otherwise un-highlights the currently highlighted item. + * @param {goog.ui.Control} item Item to highlight. + */ +goog.ui.Container.prototype.setHighlighted = function(item) { + this.setHighlightedIndex(this.indexOfChild(item)); +}; + + +/** + * Returns the currently highlighted item (if any). + * @return {goog.ui.Control?} Highlighted item (null if none). + */ +goog.ui.Container.prototype.getHighlighted = function() { + return this.getChildAt(this.highlightedIndex_); +}; + + +/** + * Highlights the first highlightable item in the container + */ +goog.ui.Container.prototype.highlightFirst = function() { + this.highlightHelper(function(index, max) { + return (index + 1) % max; + }, this.getChildCount() - 1); +}; + + +/** + * Highlights the last highlightable item in the container. + */ +goog.ui.Container.prototype.highlightLast = function() { + this.highlightHelper(function(index, max) { + index--; + return index < 0 ? max - 1 : index; + }, 0); +}; + + +/** + * Highlights the next highlightable item (or the first if nothing is currently + * highlighted). + */ +goog.ui.Container.prototype.highlightNext = function() { + this.highlightHelper(function(index, max) { + return (index + 1) % max; + }, this.highlightedIndex_); +}; + + +/** + * Highlights the previous highlightable item (or the last if nothing is + * currently highlighted). + */ +goog.ui.Container.prototype.highlightPrevious = function() { + this.highlightHelper(function(index, max) { + index--; + return index < 0 ? max - 1 : index; + }, this.highlightedIndex_); +}; + + +/** + * Helper function that manages the details of moving the highlight among + * child controls in response to keyboard events. + * @param {function(this: goog.ui.Container, number, number) : number} fn + * Function that accepts the current and maximum indices, and returns the + * next index to check. + * @param {number} startIndex Start index. + * @return {boolean} Whether the highlight has changed. + * @protected + */ +goog.ui.Container.prototype.highlightHelper = function(fn, startIndex) { + // If the start index is -1 (meaning there's nothing currently highlighted), + // try starting from the currently open item, if any. + var curIndex = + startIndex < 0 ? this.indexOfChild(this.openItem_) : startIndex; + var numItems = this.getChildCount(); + + curIndex = fn.call(this, curIndex, numItems); + var visited = 0; + while (visited <= numItems) { + var control = this.getChildAt(curIndex); + if (control && this.canHighlightItem(control)) { + this.setHighlightedIndexFromKeyEvent(curIndex); + return true; + } + visited++; + curIndex = fn.call(this, curIndex, numItems); + } + return false; +}; + + +/** + * Returns whether the given item can be highlighted. + * @param {goog.ui.Control} item The item to check. + * @return {boolean} Whether the item can be highlighted. + * @protected + */ +goog.ui.Container.prototype.canHighlightItem = function(item) { + return item.isVisible() && item.isEnabled() && + item.isSupportedState(goog.ui.Component.State.HOVER); +}; + + +/** + * Helper method that sets the highlighted index to the given index in response + * to a keyboard event. The base class implementation simply calls the + * {@link #setHighlightedIndex} method, but subclasses can override this + * behavior as needed. + * @param {number} index Index of item to highlight. + * @protected + */ +goog.ui.Container.prototype.setHighlightedIndexFromKeyEvent = function(index) { + this.setHighlightedIndex(index); +}; + + +/** + * Returns the currently open (expanded) control in the container (null if + * none). + * @return {goog.ui.Control?} The currently open control. + */ +goog.ui.Container.prototype.getOpenItem = function() { + return this.openItem_; +}; + + +/** + * Returns true if the mouse button is pressed, false otherwise. + * @return {boolean} Whether the mouse button is pressed. + */ +goog.ui.Container.prototype.isMouseButtonPressed = function() { + return this.mouseButtonPressed_; +}; + + +/** + * Sets or clears the "mouse button pressed" flag. + * @param {boolean} pressed Whether the mouse button is presed. + */ +goog.ui.Container.prototype.setMouseButtonPressed = function(pressed) { + this.mouseButtonPressed_ = pressed; +}; diff --git a/closure-library/closure/goog/ui/containerrenderer.js b/closure-library/closure/goog/ui/containerrenderer.js new file mode 100644 index 0000000000..b060d5e8f7 --- /dev/null +++ b/closure-library/closure/goog/ui/containerrenderer.js @@ -0,0 +1,377 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Base class for container renderers. + * + * @author attila@google.com (Attila Bodis) + */ + +goog.provide('goog.ui.ContainerRenderer'); + +goog.require('goog.a11y.aria'); +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.classlist'); +goog.require('goog.string'); +goog.require('goog.style'); +goog.require('goog.ui.registry'); +goog.require('goog.userAgent'); + +goog.forwardDeclare('goog.ui.Container'); +goog.forwardDeclare('goog.ui.Container.Orientation'); +goog.forwardDeclare('goog.ui.Control'); + + + +/** + * Default renderer for {@link goog.ui.Container}. Can be used as-is, but + * subclasses of Container will probably want to use renderers specifically + * tailored for them by extending this class. + * @param {string=} opt_ariaRole Optional ARIA role used for the element. + * @constructor + */ +goog.ui.ContainerRenderer = function(opt_ariaRole) { + // By default, the ARIA role is unspecified. + /** @private {string|undefined} */ + this.ariaRole_ = opt_ariaRole; +}; +goog.addSingletonGetter(goog.ui.ContainerRenderer); + + +/** + * Constructs a new renderer and sets the CSS class that the renderer will use + * as the base CSS class to apply to all elements rendered by that renderer. + * An example to use this function using a menu is: + * + *
      + * var myCustomRenderer = goog.ui.ContainerRenderer.getCustomRenderer(
      + *     goog.ui.MenuRenderer, 'my-special-menu');
      + * var newMenu = new goog.ui.Menu(opt_domHelper, myCustomRenderer);
      + * 
      + * + * Your styles for the menu can now be: + *
      + * .my-special-menu { }
      + * 
      + * + * instead of + *
      + * .CSS_MY_SPECIAL_MENU .goog-menu { }
      + * 
      + * + * You would want to use this functionality when you want an instance of a + * component to have specific styles different than the other components of the + * same type in your application. This avoids using descendant selectors to + * apply the specific styles to this component. + * + * @param {Function} ctor The constructor of the renderer you want to create. + * @param {string} cssClassName The name of the CSS class for this renderer. + * @return {goog.ui.ContainerRenderer} An instance of the desired renderer with + * its getCssClass() method overridden to return the supplied custom CSS + * class name. + */ +goog.ui.ContainerRenderer.getCustomRenderer = function(ctor, cssClassName) { + var renderer = new ctor(); + + /** + * Returns the CSS class to be applied to the root element of components + * rendered using this renderer. + * @return {string} Renderer-specific CSS class. + */ + renderer.getCssClass = function() { return cssClassName; }; + + return renderer; +}; + + +/** + * Default CSS class to be applied to the root element of containers rendered + * by this renderer. + * @type {string} + */ +goog.ui.ContainerRenderer.CSS_CLASS = goog.getCssName('goog-container'); + + +/** + * Returns the ARIA role to be applied to the container. + * See http://wiki/Main/ARIA for more info. + * @return {undefined|string} ARIA role. + */ +goog.ui.ContainerRenderer.prototype.getAriaRole = function() { + return this.ariaRole_; +}; + + +/** + * Enables or disables the tab index of the element. Only elements with a + * valid tab index can receive focus. + * @param {Element} element Element whose tab index is to be changed. + * @param {boolean} enable Whether to add or remove the element's tab index. + */ +goog.ui.ContainerRenderer.prototype.enableTabIndex = function(element, enable) { + if (element) { + element.tabIndex = enable ? 0 : -1; + } +}; + + +/** + * Creates and returns the container's root element. The default + * simply creates a DIV and applies the renderer's own CSS class name to it. + * To be overridden in subclasses. + * @param {goog.ui.Container} container Container to render. + * @return {Element} Root element for the container. + */ +goog.ui.ContainerRenderer.prototype.createDom = function(container) { + return container.getDomHelper().createDom( + goog.dom.TagName.DIV, this.getClassNames(container).join(' ')); +}; + + +/** + * Returns the DOM element into which child components are to be rendered, + * or null if the container hasn't been rendered yet. + * @param {Element} element Root element of the container whose content element + * is to be returned. + * @return {Element} Element to contain child elements (null if none). + */ +goog.ui.ContainerRenderer.prototype.getContentElement = function(element) { + return element; +}; + + +/** + * Default implementation of `canDecorate`; returns true if the element + * is a DIV, false otherwise. + * @param {Element} element Element to decorate. + * @return {boolean} Whether the renderer can decorate the element. + */ +goog.ui.ContainerRenderer.prototype.canDecorate = function(element) { + return element.tagName == 'DIV'; +}; + + +/** + * Default implementation of `decorate` for {@link goog.ui.Container}s. + * Decorates the element with the container, and attempts to decorate its child + * elements. Returns the decorated element. + * @param {goog.ui.Container} container Container to decorate the element. + * @param {Element} element Element to decorate. + * @return {!Element} Decorated element. + */ +goog.ui.ContainerRenderer.prototype.decorate = function(container, element) { + // Set the container's ID to the decorated element's DOM ID, if any. + if (element.id) { + container.setId(element.id); + } + + // Configure the container's state based on the CSS class names it has. + var baseClass = this.getCssClass(); + var hasBaseClass = false; + var classNames = goog.dom.classlist.get(element); + if (classNames) { + goog.array.forEach(classNames, function(className) { + if (className == baseClass) { + hasBaseClass = true; + } else if (className) { + this.setStateFromClassName(container, className, baseClass); + } + }, this); + } + + if (!hasBaseClass) { + // Make sure the container's root element has the renderer's own CSS class. + goog.dom.classlist.add(element, baseClass); + } + + // Decorate the element's children, if applicable. This should happen after + // the container's own state has been initialized, since how children are + // decorated may depend on the state of the container. + this.decorateChildren(container, this.getContentElement(element)); + + return element; +}; + + +/** + * Sets the container's state based on the given CSS class name, encountered + * during decoration. CSS class names that don't represent container states + * are ignored. Considered protected; subclasses should override this method + * to support more states and CSS class names. + * @param {goog.ui.Container} container Container to update. + * @param {string} className CSS class name. + * @param {string} baseClass Base class name used as the root of state-specific + * class names (typically the renderer's own class name). + * @protected + * @suppress {missingRequire} goog.ui.Container + */ +goog.ui.ContainerRenderer.prototype.setStateFromClassName = function( + container, className, baseClass) { + if (className == goog.getCssName(baseClass, 'disabled')) { + container.setEnabled(false); + } else if (className == goog.getCssName(baseClass, 'horizontal')) { + container.setOrientation(goog.ui.Container.Orientation.HORIZONTAL); + } else if (className == goog.getCssName(baseClass, 'vertical')) { + container.setOrientation(goog.ui.Container.Orientation.VERTICAL); + } +}; + + +/** + * Takes a container and an element that may contain child elements, decorates + * the child elements, and adds the corresponding components to the container + * as child components. Any non-element child nodes (e.g. empty text nodes + * introduced by line breaks in the HTML source) are removed from the element. + * @param {goog.ui.Container} container Container whose children are to be + * discovered. + * @param {Element} element Element whose children are to be decorated. + * @param {Element=} opt_firstChild the first child to be decorated. + */ +goog.ui.ContainerRenderer.prototype.decorateChildren = function( + container, element, opt_firstChild) { + if (element) { + var node = opt_firstChild || element.firstChild, next; + // Tag soup HTML may result in a DOM where siblings have different parents. + while (node && node.parentNode == element) { + // Get the next sibling here, since the node may be replaced or removed. + next = node.nextSibling; + if (node.nodeType == goog.dom.NodeType.ELEMENT) { + // Decorate element node. + var child = this.getDecoratorForChild(/** @type {!Element} */ (node)); + if (child) { + // addChild() may need to look at the element. + child.setElementInternal(/** @type {!Element} */ (node)); + // If the container is disabled, mark the child disabled too. See + // bug 1263729. Note that this must precede the call to addChild(). + if (!container.isEnabled()) { + child.setEnabled(false); + } + container.addChild(child); + child.decorate(/** @type {!Element} */ (node)); + } + } else if (!node.nodeValue || goog.string.trim(node.nodeValue) == '') { + // Remove empty text node, otherwise madness ensues (e.g. controls that + // use goog-inline-block will flicker and shift on hover on Gecko). + element.removeChild(node); + } + node = next; + } + } +}; + + +/** + * Inspects the element, and creates an instance of {@link goog.ui.Control} or + * an appropriate subclass best suited to decorate it. Returns the control (or + * null if no suitable class was found). This default implementation uses the + * element's CSS class to find the appropriate control class to instantiate. + * May be overridden in subclasses. + * @param {Element} element Element to decorate. + * @return {goog.ui.Control?} A new control suitable to decorate the element + * (null if none). + */ +goog.ui.ContainerRenderer.prototype.getDecoratorForChild = function(element) { + return /** @type {goog.ui.Control} */ ( + goog.ui.registry.getDecorator(element)); +}; + + +/** + * Initializes the container's DOM when the container enters the document. + * Called from {@link goog.ui.Container#enterDocument}. + * @param {goog.ui.Container} container Container whose DOM is to be initialized + * as it enters the document. + */ +goog.ui.ContainerRenderer.prototype.initializeDom = function(container) { + var elem = container.getElement(); + goog.asserts.assert(elem, 'The container DOM element cannot be null.'); + // Make sure the container's element isn't selectable. On Gecko, recursively + // marking each child element unselectable is expensive and unnecessary, so + // only mark the root element unselectable. + goog.style.setUnselectable(elem, true, goog.userAgent.GECKO); + + // IE doesn't support outline:none, so we have to use the hideFocus property. + if (goog.userAgent.IE) { + elem.hideFocus = true; + } + + // Set the ARIA role. + var ariaRole = this.getAriaRole(); + if (ariaRole) { + goog.a11y.aria.setRole(elem, ariaRole); + } +}; + + +/** + * Returns the element within the container's DOM that should receive keyboard + * focus (null if none). The default implementation returns the container's + * root element. + * @param {goog.ui.Container} container Container whose key event target is + * to be returned. + * @return {Element} Key event target (null if none). + */ +goog.ui.ContainerRenderer.prototype.getKeyEventTarget = function(container) { + return container.getElement(); +}; + + +/** + * Returns the CSS class to be applied to the root element of containers + * rendered using this renderer. + * @return {string} Renderer-specific CSS class. + */ +goog.ui.ContainerRenderer.prototype.getCssClass = function() { + return goog.ui.ContainerRenderer.CSS_CLASS; +}; + + +/** + * Returns all CSS class names applicable to the given container, based on its + * state. The array of class names returned includes the renderer's own CSS + * class, followed by a CSS class indicating the container's orientation, + * followed by any state-specific CSS classes. + * @param {goog.ui.Container} container Container whose CSS classes are to be + * returned. + * @return {!Array} Array of CSS class names applicable to the + * container. + */ +goog.ui.ContainerRenderer.prototype.getClassNames = function(container) { + var baseClass = this.getCssClass(); + var isHorizontal = + container.getOrientation() == goog.ui.Container.Orientation.HORIZONTAL; + var classNames = [ + baseClass, (isHorizontal ? goog.getCssName(baseClass, 'horizontal') : + goog.getCssName(baseClass, 'vertical')) + ]; + if (!container.isEnabled()) { + classNames.push(goog.getCssName(baseClass, 'disabled')); + } + return classNames; +}; + + +/** + * Returns the default orientation of containers rendered or decorated by this + * renderer. The base class implementation returns `VERTICAL`. + * @return {goog.ui.Container.Orientation} Default orientation for containers + * created or decorated by this renderer. + * @suppress {missingRequire} goog.ui.Container + */ +goog.ui.ContainerRenderer.prototype.getDefaultOrientation = function() { + return goog.ui.Container.Orientation.VERTICAL; +}; diff --git a/closure-library/closure/goog/ui/containerscroller.js b/closure-library/closure/goog/ui/containerscroller.js new file mode 100644 index 0000000000..63ea29aa01 --- /dev/null +++ b/closure-library/closure/goog/ui/containerscroller.js @@ -0,0 +1,221 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Scroll behavior that can be added onto a container. + * @author gboyer@google.com (Garry Boyer) + */ + +goog.provide('goog.ui.ContainerScroller'); + +goog.require('goog.Disposable'); +goog.require('goog.Timer'); +goog.require('goog.events.EventHandler'); +goog.require('goog.style'); +goog.require('goog.ui.Component'); +goog.require('goog.ui.Container'); + + + +/** + * Plug-on scrolling behavior for a container. + * + * Use this to style containers, such as pop-up menus, to be scrolling, and + * automatically keep the highlighted element visible. + * + * To use this, first style your container with the desired overflow + * properties and height to achieve vertical scrolling. Also, the scrolling + * div should have no vertical padding, for two reasons: it is difficult to + * compensate for, and is generally not what you want due to the strange way + * CSS handles padding on the scrolling dimension. + * + * The container must already be rendered before this may be constructed. + * + * @param {!goog.ui.Container} container The container to attach behavior to. + * @constructor + * @extends {goog.Disposable} + * @final + */ +goog.ui.ContainerScroller = function(container) { + goog.Disposable.call(this); + + /** + * The container that we are bestowing scroll behavior on. + * @type {!goog.ui.Container} + * @private + */ + this.container_ = container; + + /** + * Event handler for this object. + * @type {!goog.events.EventHandler} + * @private + */ + this.eventHandler_ = new goog.events.EventHandler(this); + + this.eventHandler_.listen( + container, goog.ui.Component.EventType.HIGHLIGHT, this.onHighlight_); + this.eventHandler_.listen( + container, goog.ui.Component.EventType.ENTER, this.onEnter_); + this.eventHandler_.listen( + container, goog.ui.Container.EventType.AFTER_SHOW, this.onAfterShow_); + this.eventHandler_.listen( + container, goog.ui.Component.EventType.HIDE, this.onHide_); + + // TODO(gboyer): Allow a ContainerScroller to be attached with a Container + // before the container is rendered. + + this.doScrolling_(true); +}; +goog.inherits(goog.ui.ContainerScroller, goog.Disposable); + + +/** + * The last target the user hovered over. + * + * @see #onEnter_ + * @type {?goog.ui.Component} + * @private + */ +goog.ui.ContainerScroller.prototype.lastEnterTarget_ = null; + + +/** + * The scrollTop of the container before it was hidden. + * Used to restore the scroll position when the container is shown again. + * @type {?number} + * @private + */ +goog.ui.ContainerScroller.prototype.scrollTopBeforeHide_ = null; + + +/** + * Whether we are disabling the default handler for hovering. + * + * @see #onEnter_ + * @see #temporarilyDisableHover_ + * @type {boolean} + * @private + */ +goog.ui.ContainerScroller.prototype.disableHover_ = false; + + +/** + * Handles hover events on the container's children. + * + * Helps enforce two constraints: scrolling should not cause mouse highlights, + * and mouse highlights should not cause scrolling. + * + * @param {goog.events.Event} e The container's ENTER event. + * @private + */ +goog.ui.ContainerScroller.prototype.onEnter_ = function(e) { + if (this.disableHover_) { + // The container was scrolled recently. Since the mouse may be over the + // container, stop the default action of the ENTER event from causing + // highlights. + e.preventDefault(); + } else { + // The mouse is moving and causing hover events. Stop the resulting + // highlight (if it happens) from causing a scroll. + this.lastEnterTarget_ = /** @type {goog.ui.Component} */ (e.target); + } +}; + + +/** + * Handles highlight events on the container's children. + * @param {goog.events.Event} e The container's highlight event. + * @private + */ +goog.ui.ContainerScroller.prototype.onHighlight_ = function(e) { + this.doScrolling_(); +}; + + +/** + * Handles AFTER_SHOW events on the container. Makes the container + * scroll to the previously scrolled position (if there was one), + * then adjust it to make the highlighted element be in view (if there is one). + * If there was no previous scroll position, then center the highlighted + * element (if there is one). + * @param {goog.events.Event} e The container's AFTER_SHOW event. + * @private + */ +goog.ui.ContainerScroller.prototype.onAfterShow_ = function(e) { + if (this.scrollTopBeforeHide_ != null) { + this.container_.getElement().scrollTop = this.scrollTopBeforeHide_; + // Make sure the highlighted item is still visible, in case the list + // or its hilighted item has changed. + this.doScrolling_(false); + } else { + this.doScrolling_(true); + } +}; + + +/** + * Handles hide events on the container. Clears out the last enter target, + * since it is no longer applicable, and remembers the scroll position of + * the menu so that it can be restored when the menu is reopened. + * @param {goog.events.Event} e The container's hide event. + * @private + */ +goog.ui.ContainerScroller.prototype.onHide_ = function(e) { + if (e.target == this.container_) { + this.lastEnterTarget_ = null; + this.scrollTopBeforeHide_ = this.container_.getElement().scrollTop; + } +}; + + +/** + * Centers the currently highlighted item, if this is scrollable. + * @param {boolean=} opt_center Whether to center the highlighted element + * rather than simply ensure it is in view. Useful for the first + * render. + * @private + */ +goog.ui.ContainerScroller.prototype.doScrolling_ = function(opt_center) { + var highlighted = this.container_.getHighlighted(); + + // Only scroll if we're visible and there is a highlighted item. + if (this.container_.isVisible() && highlighted && + highlighted != this.lastEnterTarget_) { + var element = this.container_.getElement(); + goog.style.scrollIntoContainerView( + highlighted.getElement(), element, opt_center); + this.temporarilyDisableHover_(); + this.lastEnterTarget_ = null; + } +}; + + +/** + * Temporarily disables hover events from changing highlight. + * @see #onEnter_ + * @private + */ +goog.ui.ContainerScroller.prototype.temporarilyDisableHover_ = function() { + this.disableHover_ = true; + goog.Timer.callOnce(function() { this.disableHover_ = false; }, 0, this); +}; + + +/** @override */ +goog.ui.ContainerScroller.prototype.disposeInternal = function() { + goog.ui.ContainerScroller.superClass_.disposeInternal.call(this); + this.eventHandler_.dispose(); + this.lastEnterTarget_ = null; +}; diff --git a/closure-library/closure/goog/ui/control.js b/closure-library/closure/goog/ui/control.js new file mode 100644 index 0000000000..627f9dea07 --- /dev/null +++ b/closure-library/closure/goog/ui/control.js @@ -0,0 +1,1613 @@ +// Copyright 2007 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 Base class for UI controls such as buttons, menus, menu items, + * toolbar buttons, etc. The implementation is based on a generalized version + * of {@link goog.ui.MenuItem}. + * TODO(attila): If the renderer framework works well, pull it into Component. + * + * @author attila@google.com (Attila Bodis) + * @see ../demos/control.html + * @see http://code.google.com/p/closure-library/wiki/IntroToControls + */ + +goog.provide('goog.ui.Control'); + +goog.require('goog.Disposable'); +goog.require('goog.array'); +goog.require('goog.dom'); +goog.require('goog.events.BrowserEvent'); +goog.require('goog.events.Event'); +goog.require('goog.events.EventHandler'); +goog.require('goog.events.EventType'); +goog.require('goog.events.KeyCodes'); +goog.require('goog.events.KeyHandler'); +goog.require('goog.string'); +goog.require('goog.ui.Component'); +goog.require('goog.ui.ComponentUtil'); +/** @suppress {extraRequire} */ +goog.require('goog.ui.ControlContent'); +goog.require('goog.ui.ControlRenderer'); +goog.require('goog.ui.registry'); +goog.require('goog.userAgent'); + + + +/** + * Base class for UI controls. Extends {@link goog.ui.Component} by adding + * the following: + *
        + *
      • a {@link goog.events.KeyHandler}, to simplify keyboard handling, + *
      • a pluggable renderer framework, to simplify the creation of + * simple controls without the need to subclass this class, + *
      • the notion of component content, like a text caption or DOM + * structure displayed in the component (e.g. a button label), + *
      • getter and setter for component content, as well as a getter and + * setter specifically for caption text (for convenience), + *
      • support for hiding/showing the component, +
      • fine-grained control over supported states and state transition + events, and + *
      • default mouse and keyboard event handling. + *
      + * This class has sufficient built-in functionality for most simple UI controls. + * All controls dispatch SHOW, HIDE, ENTER, LEAVE, and ACTION events on show, + * hide, mouseover, mouseout, and user action, respectively. Additional states + * are also supported. See closure/demos/control.html + * for example usage. + * @param {goog.ui.ControlContent=} opt_content Text caption or DOM structure + * to display as the content of the control (if any). + * @param {goog.ui.ControlRenderer=} opt_renderer Renderer used to render or + * decorate the component; defaults to {@link goog.ui.ControlRenderer}. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for + * document interaction. + * @constructor + * @extends {goog.ui.Component} + */ +goog.ui.Control = function(opt_content, opt_renderer, opt_domHelper) { + goog.ui.Component.call(this, opt_domHelper); + this.renderer_ = + opt_renderer || goog.ui.registry.getDefaultRenderer(this.constructor); + this.setContentInternal(goog.isDef(opt_content) ? opt_content : null); + + /** @private {?string} The control's aria-label. */ + this.ariaLabel_ = null; + + /** @private {goog.ui.Control.IeMouseEventSequenceSimulator_} */ + this.ieMouseEventSequenceSimulator_; +}; +goog.inherits(goog.ui.Control, goog.ui.Component); +goog.tagUnsealableClass(goog.ui.Control); + + +// Renderer registry. +// TODO(attila): Refactor existing usages inside Google in a follow-up CL. + + +/** + * Maps a CSS class name to a function that returns a new instance of + * {@link goog.ui.Control} or a subclass thereof, suitable to decorate + * an element that has the specified CSS class. UI components that extend + * {@link goog.ui.Control} and want {@link goog.ui.Container}s to be able + * to discover and decorate elements using them should register a factory + * function via this API. + * @param {string} className CSS class name. + * @param {Function} decoratorFunction Function that takes no arguments and + * returns a new instance of a control to decorate an element with the + * given class. + * @deprecated Use {@link goog.ui.registry.setDecoratorByClassName} instead. + */ +goog.ui.Control.registerDecorator = goog.ui.registry.setDecoratorByClassName; + + +/** + * Takes an element and returns a new instance of {@link goog.ui.Control} + * or a subclass, suitable to decorate it (based on the element's CSS class). + * @param {Element} element Element to decorate. + * @return {goog.ui.Control?} New control instance to decorate the element + * (null if none). + * @deprecated Use {@link goog.ui.registry.getDecorator} instead. + */ +goog.ui.Control.getDecorator = + /** @type {function(Element): goog.ui.Control} */ ( + goog.ui.registry.getDecorator); + + +/** + * Renderer associated with the component. + * @type {goog.ui.ControlRenderer|undefined} + * @private + */ +goog.ui.Control.prototype.renderer_; + + +/** + * Text caption or DOM structure displayed in the component. + * @type {?goog.ui.ControlContent} + * @private + */ +goog.ui.Control.prototype.content_ = null; + + +/** + * Current component state; a bit mask of {@link goog.ui.Component.State}s. + * @type {number} + * @private + */ +goog.ui.Control.prototype.state_ = 0x00; + + +/** + * A bit mask of {@link goog.ui.Component.State}s this component supports. + * @type {number} + * @private + */ +goog.ui.Control.prototype.supportedStates_ = goog.ui.Component.State.DISABLED | + goog.ui.Component.State.HOVER | goog.ui.Component.State.ACTIVE | + goog.ui.Component.State.FOCUSED; + + +/** + * A bit mask of {@link goog.ui.Component.State}s for which this component + * provides default event handling. For example, a component that handles + * the HOVER state automatically will highlight itself on mouseover, whereas + * a component that doesn't handle HOVER automatically will only dispatch + * ENTER and LEAVE events but not call {@link setHighlighted} on itself. + * By default, components provide default event handling for all states. + * Controls hosted in containers (e.g. menu items in a menu, or buttons in a + * toolbar) will typically want to have their container manage their highlight + * state. Selectable controls managed by a selection model will also typically + * want their selection state to be managed by the model. + * @type {number} + * @private + */ +goog.ui.Control.prototype.autoStates_ = goog.ui.Component.State.ALL; + + +/** + * A bit mask of {@link goog.ui.Component.State}s for which this component + * dispatches state transition events. Because events are expensive, the + * default behavior is to not dispatch any state transition events at all. + * Use the {@link #setDispatchTransitionEvents} API to request transition + * events as needed. Subclasses may enable transition events by default. + * Controls hosted in containers or managed by a selection model will typically + * want to dispatch transition events. + * @type {number} + * @private + */ +goog.ui.Control.prototype.statesWithTransitionEvents_ = 0x00; + + +/** + * Component visibility. + * @type {boolean} + * @private + */ +goog.ui.Control.prototype.visible_ = true; + + +/** + * Keyboard event handler. + * @type {goog.events.KeyHandler} + * @private + */ +goog.ui.Control.prototype.keyHandler_; + + +/** + * Additional class name(s) to apply to the control's root element, if any. + * @type {Array?} + * @private + */ +goog.ui.Control.prototype.extraClassNames_ = null; + + +/** + * Whether the control should listen for and handle mouse events; defaults to + * true. + * @type {boolean} + * @private + */ +goog.ui.Control.prototype.handleMouseEvents_ = true; + + +/** + * Whether the control allows text selection within its DOM. Defaults to false. + * @type {boolean} + * @private + */ +goog.ui.Control.prototype.allowTextSelection_ = false; + + +/** + * The control's preferred ARIA role. + * @type {?goog.a11y.aria.Role} + * @private + */ +goog.ui.Control.prototype.preferredAriaRole_ = null; + + +// Event handler and renderer management. + + +/** + * Returns true if the control is configured to handle its own mouse events, + * false otherwise. Controls not hosted in {@link goog.ui.Container}s have + * to handle their own mouse events, but controls hosted in containers may + * allow their parent to handle mouse events on their behalf. Considered + * protected; should only be used within this package and by subclasses. + * @return {boolean} Whether the control handles its own mouse events. + */ +goog.ui.Control.prototype.isHandleMouseEvents = function() { + return this.handleMouseEvents_; +}; + + +/** + * Enables or disables mouse event handling for the control. Containers may + * use this method to disable mouse event handling in their child controls. + * Considered protected; should only be used within this package and by + * subclasses. + * @param {boolean} enable Whether to enable or disable mouse event handling. + */ +goog.ui.Control.prototype.setHandleMouseEvents = function(enable) { + if (this.isInDocument() && enable != this.handleMouseEvents_) { + // Already in the document; need to update event handler. + this.enableMouseEventHandling_(enable); + } + this.handleMouseEvents_ = enable; +}; + + +/** + * Returns the DOM element on which the control is listening for keyboard + * events (null if none). + * @return {Element} Element on which the control is listening for key + * events. + */ +goog.ui.Control.prototype.getKeyEventTarget = function() { + // Delegate to renderer. + return this.renderer_.getKeyEventTarget(this); +}; + + +/** + * Returns the keyboard event handler for this component, lazily created the + * first time this method is called. Considered protected; should only be + * used within this package and by subclasses. + * @return {!goog.events.KeyHandler} Keyboard event handler for this component. + * @protected + */ +goog.ui.Control.prototype.getKeyHandler = function() { + return this.keyHandler_ || (this.keyHandler_ = new goog.events.KeyHandler()); +}; + + +/** + * Returns the renderer used by this component to render itself or to decorate + * an existing element. + * @return {goog.ui.ControlRenderer|undefined} Renderer used by the component + * (undefined if none). + */ +goog.ui.Control.prototype.getRenderer = function() { + return this.renderer_; +}; + + +/** + * Registers the given renderer with the component. Changing renderers after + * the component has entered the document is an error. + * @param {goog.ui.ControlRenderer} renderer Renderer used by the component. + * @throws {Error} If the control is already in the document. + */ +goog.ui.Control.prototype.setRenderer = function(renderer) { + if (this.isInDocument()) { + // Too late. + throw new Error(goog.ui.Component.Error.ALREADY_RENDERED); + } + + if (this.getElement()) { + // The component has already been rendered, but isn't yet in the document. + // Replace the renderer and delete the current DOM, so it can be re-rendered + // using the new renderer the next time someone calls render(). + this.setElementInternal(null); + } + + this.renderer_ = renderer; +}; + + +// Support for additional styling. + + +/** + * Returns any additional class name(s) to be applied to the component's + * root element, or null if no extra class names are needed. + * @return {Array?} Additional class names to be applied to + * the component's root element (null if none). + */ +goog.ui.Control.prototype.getExtraClassNames = function() { + return this.extraClassNames_; +}; + + +/** + * Adds the given class name to the list of classes to be applied to the + * component's root element. + * @param {string} className Additional class name to be applied to the + * component's root element. + */ +goog.ui.Control.prototype.addClassName = function(className) { + if (className) { + if (this.extraClassNames_) { + if (!goog.array.contains(this.extraClassNames_, className)) { + this.extraClassNames_.push(className); + } + } else { + this.extraClassNames_ = [className]; + } + this.renderer_.enableExtraClassName(this, className, true); + } +}; + + +/** + * Removes the given class name from the list of classes to be applied to + * the component's root element. + * @param {string} className Class name to be removed from the component's root + * element. + */ +goog.ui.Control.prototype.removeClassName = function(className) { + if (className && this.extraClassNames_ && + goog.array.remove(this.extraClassNames_, className)) { + if (this.extraClassNames_.length == 0) { + this.extraClassNames_ = null; + } + this.renderer_.enableExtraClassName(this, className, false); + } +}; + + +/** + * Adds or removes the given class name to/from the list of classes to be + * applied to the component's root element. + * @param {string} className CSS class name to add or remove. + * @param {boolean} enable Whether to add or remove the class name. + */ +goog.ui.Control.prototype.enableClassName = function(className, enable) { + if (enable) { + this.addClassName(className); + } else { + this.removeClassName(className); + } +}; + + +// Standard goog.ui.Component implementation. + + +/** + * Creates the control's DOM. Overrides {@link goog.ui.Component#createDom} by + * delegating DOM manipulation to the control's renderer. + * @override + */ +goog.ui.Control.prototype.createDom = function() { + var element = this.renderer_.createDom(this); + this.setElementInternal(element); + + // Initialize ARIA role. + this.renderer_.setAriaRole(element, this.getPreferredAriaRole()); + + // Initialize text selection. + if (!this.isAllowTextSelection()) { + // The renderer is assumed to create selectable elements. Since making + // elements unselectable is expensive, only do it if needed (bug 1037090). + this.renderer_.setAllowTextSelection(element, false); + } + + // Initialize visibility. + if (!this.isVisible()) { + // The renderer is assumed to create visible elements. Since hiding + // elements can be expensive, only do it if needed (bug 1037105). + this.renderer_.setVisible(element, false); + } +}; + + +/** + * Returns the control's preferred ARIA role. This can be used by a control to + * override the role that would be assigned by the renderer. This is useful in + * cases where a different ARIA role is appropriate for a control because of the + * context in which it's used. E.g., a {@link goog.ui.MenuButton} added to a + * {@link goog.ui.Select} should have an ARIA role of LISTBOX and not MENUITEM. + * @return {?goog.a11y.aria.Role} This control's preferred ARIA role or null if + * no preferred ARIA role is set. + */ +goog.ui.Control.prototype.getPreferredAriaRole = function() { + return this.preferredAriaRole_; +}; + + +/** + * Sets the control's preferred ARIA role. This can be used to override the role + * that would be assigned by the renderer. This is useful in cases where a + * different ARIA role is appropriate for a control because of the + * context in which it's used. E.g., a {@link goog.ui.MenuButton} added to a + * {@link goog.ui.Select} should have an ARIA role of LISTBOX and not MENUITEM. + * @param {goog.a11y.aria.Role} role This control's preferred ARIA role. + */ +goog.ui.Control.prototype.setPreferredAriaRole = function(role) { + this.preferredAriaRole_ = role; +}; + + +/** + * Gets the control's aria label. + * @return {?string} This control's aria label. + */ +goog.ui.Control.prototype.getAriaLabel = function() { + return this.ariaLabel_; +}; + + +/** + * Sets the control's aria label. This can be used to assign aria label to the + * element after it is rendered. + * @param {string} label The string to set as the aria label for this control. + * No escaping is done on this value. + */ +goog.ui.Control.prototype.setAriaLabel = function(label) { + this.ariaLabel_ = label; + var element = this.getElement(); + if (element) { + this.renderer_.setAriaLabel(element, label); + } +}; + + +/** + * Returns the DOM element into which child components are to be rendered, + * or null if the control itself hasn't been rendered yet. Overrides + * {@link goog.ui.Component#getContentElement} by delegating to the renderer. + * @return {Element} Element to contain child elements (null if none). + * @override + */ +goog.ui.Control.prototype.getContentElement = function() { + // Delegate to renderer. + return this.renderer_.getContentElement(this.getElement()); +}; + + +/** + * Returns true if the given element can be decorated by this component. + * Overrides {@link goog.ui.Component#canDecorate}. + * @param {Element} element Element to decorate. + * @return {boolean} Whether the element can be decorated by this component. + * @override + */ +goog.ui.Control.prototype.canDecorate = function(element) { + // Controls support pluggable renderers; delegate to the renderer. + return this.renderer_.canDecorate(element); +}; + + +/** + * Decorates the given element with this component. Overrides {@link + * goog.ui.Component#decorateInternal} by delegating DOM manipulation + * to the control's renderer. + * @param {Element} element Element to decorate. + * @protected + * @override + */ +goog.ui.Control.prototype.decorateInternal = function(element) { + element = this.renderer_.decorate(this, element); + this.setElementInternal(element); + + // Initialize ARIA role. + this.renderer_.setAriaRole(element, this.getPreferredAriaRole()); + + // Initialize text selection. + if (!this.isAllowTextSelection()) { + // Decorated elements are assumed to be selectable. Since making elements + // unselectable is expensive, only do it if needed (bug 1037090). + this.renderer_.setAllowTextSelection(element, false); + } + + // Initialize visibility based on the decorated element's styling. + this.visible_ = element.style.display != 'none'; +}; + + +/** + * Configures the component after its DOM has been rendered, and sets up event + * handling. Overrides {@link goog.ui.Component#enterDocument}. + * @override + */ +goog.ui.Control.prototype.enterDocument = function() { + goog.ui.Control.superClass_.enterDocument.call(this); + + // Call the renderer's setAriaStates method to set element's aria attributes. + this.renderer_.setAriaStates(this, this.getElementStrict()); + + // Call the renderer's initializeDom method to configure properties of the + // control's DOM that can only be done once it's in the document. + this.renderer_.initializeDom(this); + + // Initialize event handling if at least one state other than DISABLED is + // supported. + if (this.supportedStates_ & ~goog.ui.Component.State.DISABLED) { + // Initialize mouse event handling if the control is configured to handle + // its own mouse events. (Controls hosted in containers don't need to + // handle their own mouse events.) + if (this.isHandleMouseEvents()) { + this.enableMouseEventHandling_(true); + } + + // Initialize keyboard event handling if the control is focusable and has + // a key event target. (Controls hosted in containers typically aren't + // focusable, allowing their container to handle keyboard events for them.) + if (this.isSupportedState(goog.ui.Component.State.FOCUSED)) { + var keyTarget = this.getKeyEventTarget(); + if (keyTarget) { + var keyHandler = this.getKeyHandler(); + keyHandler.attach(keyTarget); + this.getHandler() + .listen( + keyHandler, goog.events.KeyHandler.EventType.KEY, + this.handleKeyEvent) + .listen(keyTarget, goog.events.EventType.FOCUS, this.handleFocus) + .listen(keyTarget, goog.events.EventType.BLUR, this.handleBlur); + } + } + } +}; + + +/** + * Enables or disables mouse event handling on the control. + * @param {boolean} enable Whether to enable mouse event handling. + * @private + */ +goog.ui.Control.prototype.enableMouseEventHandling_ = function(enable) { + var MouseEventType = goog.ui.ComponentUtil.getMouseEventType(this); + + var handler = this.getHandler(); + var element = this.getElement(); + if (enable) { + handler.listen(element, MouseEventType.MOUSEDOWN, this.handleMouseDown) + .listen( + element, [MouseEventType.MOUSEUP, MouseEventType.MOUSECANCEL], + this.handleMouseUp) + .listen(element, goog.events.EventType.MOUSEOVER, this.handleMouseOver) + .listen(element, goog.events.EventType.MOUSEOUT, this.handleMouseOut); + if (this.pointerEventsEnabled()) { + // Prevent pointer events from capturing the target element so they behave + // more like mouse events. + handler.listen( + element, goog.events.EventType.GOTPOINTERCAPTURE, + this.preventPointerCapture_); + } + if (this.handleContextMenu != goog.nullFunction) { + handler.listen( + element, goog.events.EventType.CONTEXTMENU, this.handleContextMenu); + } + if (goog.userAgent.IE) { + // Versions of IE before 9 send only one click event followed by a + // dblclick, so we must explicitly listen for these. In later versions, + // two click events are fired and so a dblclick listener is unnecessary. + if (!goog.userAgent.isVersionOrHigher(9)) { + handler.listen( + element, goog.events.EventType.DBLCLICK, this.handleDblClick); + } + if (!this.ieMouseEventSequenceSimulator_) { + this.ieMouseEventSequenceSimulator_ = + new goog.ui.Control.IeMouseEventSequenceSimulator_(this); + this.registerDisposable(this.ieMouseEventSequenceSimulator_); + } + } + } else { + handler.unlisten(element, MouseEventType.MOUSEDOWN, this.handleMouseDown) + .unlisten( + element, [MouseEventType.MOUSEUP, MouseEventType.MOUSECANCEL], + this.handleMouseUp) + .unlisten( + element, goog.events.EventType.MOUSEOVER, this.handleMouseOver) + .unlisten(element, goog.events.EventType.MOUSEOUT, this.handleMouseOut); + if (this.pointerEventsEnabled()) { + handler.unlisten( + element, goog.events.EventType.GOTPOINTERCAPTURE, + this.preventPointerCapture_); + } + if (this.handleContextMenu != goog.nullFunction) { + handler.unlisten( + element, goog.events.EventType.CONTEXTMENU, this.handleContextMenu); + } + if (goog.userAgent.IE) { + if (!goog.userAgent.isVersionOrHigher(9)) { + handler.unlisten( + element, goog.events.EventType.DBLCLICK, this.handleDblClick); + } + goog.dispose(this.ieMouseEventSequenceSimulator_); + this.ieMouseEventSequenceSimulator_ = null; + } + } +}; + + +/** + * Cleans up the component before its DOM is removed from the document, and + * removes event handlers. Overrides {@link goog.ui.Component#exitDocument} + * by making sure that components that are removed from the document aren't + * focusable (i.e. have no tab index). + * @override + */ +goog.ui.Control.prototype.exitDocument = function() { + goog.ui.Control.superClass_.exitDocument.call(this); + if (this.keyHandler_) { + this.keyHandler_.detach(); + } + if (this.isVisible() && this.isEnabled()) { + this.renderer_.setFocusable(this, false); + } +}; + + +/** @override */ +goog.ui.Control.prototype.disposeInternal = function() { + goog.ui.Control.superClass_.disposeInternal.call(this); + if (this.keyHandler_) { + this.keyHandler_.dispose(); + delete this.keyHandler_; + } + delete this.renderer_; + this.content_ = null; + this.extraClassNames_ = null; + this.ieMouseEventSequenceSimulator_ = null; +}; + + +// Component content management. + + +/** + * Returns the text caption or DOM structure displayed in the component. + * @return {goog.ui.ControlContent} Text caption or DOM structure + * comprising the component's contents. + */ +goog.ui.Control.prototype.getContent = function() { + return this.content_; +}; + + +/** + * Sets the component's content to the given text caption, element, or array of + * nodes. (If the argument is an array of nodes, it must be an actual array, + * not an array-like object.) + * @param {goog.ui.ControlContent} content Text caption or DOM + * structure to set as the component's contents. + */ +goog.ui.Control.prototype.setContent = function(content) { + // Controls support pluggable renderers; delegate to the renderer. + this.renderer_.setContent(this.getElement(), content); + + // setContentInternal needs to be after the renderer, since the implementation + // may depend on the content being in the DOM. + this.setContentInternal(content); +}; + + +/** + * Sets the component's content to the given text caption, element, or array + * of nodes. Unlike {@link #setContent}, doesn't modify the component's DOM. + * Called by renderers during element decoration. + * + * This should only be used by subclasses and its associated renderers. + * + * @param {goog.ui.ControlContent} content Text caption or DOM structure + * to set as the component's contents. + */ +goog.ui.Control.prototype.setContentInternal = function(content) { + this.content_ = content; +}; + + +/** + * @return {string} Text caption of the control or empty string if none. + */ +goog.ui.Control.prototype.getCaption = function() { + var content = this.getContent(); + if (!content) { + return ''; + } + var caption = goog.isString(content) ? + content : + goog.isArray(content) ? + goog.array.map(content, goog.dom.getRawTextContent).join('') : + goog.dom.getTextContent(/** @type {!Node} */ (content)); + return goog.string.collapseBreakingSpaces(caption); +}; + + +/** + * Sets the text caption of the component. + * @param {string} caption Text caption of the component. + */ +goog.ui.Control.prototype.setCaption = function(caption) { + this.setContent(caption); +}; + + +// Component state management. + + +/** @override */ +goog.ui.Control.prototype.setRightToLeft = function(rightToLeft) { + // The superclass implementation ensures the control isn't in the document. + goog.ui.Control.superClass_.setRightToLeft.call(this, rightToLeft); + + var element = this.getElement(); + if (element) { + this.renderer_.setRightToLeft(element, rightToLeft); + } +}; + + +/** + * Returns true if the control allows text selection within its DOM, false + * otherwise. Controls that disallow text selection have the appropriate + * unselectable styling applied to their elements. Note that controls hosted + * in containers will report that they allow text selection even if their + * container disallows text selection. + * @return {boolean} Whether the control allows text selection. + */ +goog.ui.Control.prototype.isAllowTextSelection = function() { + return this.allowTextSelection_; +}; + + +/** + * Allows or disallows text selection within the control's DOM. + * @param {boolean} allow Whether the control should allow text selection. + */ +goog.ui.Control.prototype.setAllowTextSelection = function(allow) { + this.allowTextSelection_ = allow; + + var element = this.getElement(); + if (element) { + this.renderer_.setAllowTextSelection(element, allow); + } +}; + + +/** + * Returns true if the component's visibility is set to visible, false if + * it is set to hidden. A component that is set to hidden is guaranteed + * to be hidden from the user, but the reverse isn't necessarily true. + * A component may be set to visible but can otherwise be obscured by another + * element, rendered off-screen, or hidden using direct CSS manipulation. + * @return {boolean} Whether the component is visible. + */ +goog.ui.Control.prototype.isVisible = function() { + return this.visible_; +}; + + +/** + * Shows or hides the component. Does nothing if the component already has + * the requested visibility. Otherwise, dispatches a SHOW or HIDE event as + * appropriate, giving listeners a chance to prevent the visibility change. + * When showing a component that is both enabled and focusable, ensures that + * its key target has a tab index. When hiding a component that is enabled + * and focusable, blurs its key target and removes its tab index. + * @param {boolean} visible Whether to show or hide the component. + * @param {boolean=} opt_force If true, doesn't check whether the component + * already has the requested visibility, and doesn't dispatch any events. + * @return {boolean} Whether the visibility was changed. + */ +goog.ui.Control.prototype.setVisible = function(visible, opt_force) { + if (opt_force || (this.visible_ != visible && + this.dispatchEvent( + visible ? goog.ui.Component.EventType.SHOW : + goog.ui.Component.EventType.HIDE))) { + var element = this.getElement(); + if (element) { + this.renderer_.setVisible(element, visible); + } + if (this.isEnabled()) { + this.renderer_.setFocusable(this, visible); + } + this.visible_ = visible; + return true; + } + return false; +}; + + +/** + * Returns true if the component is enabled, false otherwise. + * @return {boolean} Whether the component is enabled. + */ +goog.ui.Control.prototype.isEnabled = function() { + return !this.hasState(goog.ui.Component.State.DISABLED); +}; + + +/** + * Returns true if the control has a parent that is itself disabled, false + * otherwise. + * @return {boolean} Whether the component is hosted in a disabled container. + * @private + */ +goog.ui.Control.prototype.isParentDisabled_ = function() { + var parent = this.getParent(); + return !!parent && typeof parent.isEnabled == 'function' && + !parent.isEnabled(); +}; + + +/** + * Enables or disables the component. Does nothing if this state transition + * is disallowed. If the component is both visible and focusable, updates its + * focused state and tab index as needed. If the component is being disabled, + * ensures that it is also deactivated and un-highlighted first. Note that the + * component's enabled/disabled state is "locked" as long as it is hosted in a + * {@link goog.ui.Container} that is itself disabled; this is to prevent clients + * from accidentally re-enabling a control that is in a disabled container. + * @param {boolean} enable Whether to enable or disable the component. + * @see #isTransitionAllowed + */ +goog.ui.Control.prototype.setEnabled = function(enable) { + if (!this.isParentDisabled_() && + this.isTransitionAllowed(goog.ui.Component.State.DISABLED, !enable)) { + if (!enable) { + this.setActive(false); + this.setHighlighted(false); + } + if (this.isVisible()) { + this.renderer_.setFocusable(this, enable); + } + this.setState(goog.ui.Component.State.DISABLED, !enable, true); + } +}; + + +/** + * Returns true if the component is currently highlighted, false otherwise. + * @return {boolean} Whether the component is highlighted. + */ +goog.ui.Control.prototype.isHighlighted = function() { + return this.hasState(goog.ui.Component.State.HOVER); +}; + + +/** + * Highlights or unhighlights the component. Does nothing if this state + * transition is disallowed. + * @param {boolean} highlight Whether to highlight or unhighlight the component. + * @see #isTransitionAllowed + */ +goog.ui.Control.prototype.setHighlighted = function(highlight) { + if (this.isTransitionAllowed(goog.ui.Component.State.HOVER, highlight)) { + this.setState(goog.ui.Component.State.HOVER, highlight); + } +}; + + +/** + * Returns true if the component is active (pressed), false otherwise. + * @return {boolean} Whether the component is active. + */ +goog.ui.Control.prototype.isActive = function() { + return this.hasState(goog.ui.Component.State.ACTIVE); +}; + + +/** + * Activates or deactivates the component. Does nothing if this state + * transition is disallowed. + * @param {boolean} active Whether to activate or deactivate the component. + * @see #isTransitionAllowed + */ +goog.ui.Control.prototype.setActive = function(active) { + if (this.isTransitionAllowed(goog.ui.Component.State.ACTIVE, active)) { + this.setState(goog.ui.Component.State.ACTIVE, active); + } +}; + + +/** + * Returns true if the component is selected, false otherwise. + * @return {boolean} Whether the component is selected. + */ +goog.ui.Control.prototype.isSelected = function() { + return this.hasState(goog.ui.Component.State.SELECTED); +}; + + +/** + * Selects or unselects the component. Does nothing if this state transition + * is disallowed. + * @param {boolean} select Whether to select or unselect the component. + * @see #isTransitionAllowed + */ +goog.ui.Control.prototype.setSelected = function(select) { + if (this.isTransitionAllowed(goog.ui.Component.State.SELECTED, select)) { + this.setState(goog.ui.Component.State.SELECTED, select); + } +}; + + +/** + * Returns true if the component is checked, false otherwise. + * @return {boolean} Whether the component is checked. + */ +goog.ui.Control.prototype.isChecked = function() { + return this.hasState(goog.ui.Component.State.CHECKED); +}; + + +/** + * Checks or unchecks the component. Does nothing if this state transition + * is disallowed. + * @param {boolean} check Whether to check or uncheck the component. + * @see #isTransitionAllowed + */ +goog.ui.Control.prototype.setChecked = function(check) { + if (this.isTransitionAllowed(goog.ui.Component.State.CHECKED, check)) { + this.setState(goog.ui.Component.State.CHECKED, check); + } +}; + + +/** + * Returns true if the component is styled to indicate that it has keyboard + * focus, false otherwise. Note that `isFocused()` returning true + * doesn't guarantee that the component's key event target has keyboard focus, + * only that it is styled as such. + * @return {boolean} Whether the component is styled to indicate as having + * keyboard focus. + */ +goog.ui.Control.prototype.isFocused = function() { + return this.hasState(goog.ui.Component.State.FOCUSED); +}; + + +/** + * Applies or removes styling indicating that the component has keyboard focus. + * Note that unlike the other "set" methods, this method is called as a result + * of the component's element having received or lost keyboard focus, not the + * other way around, so calling `setFocused(true)` doesn't guarantee that + * the component's key event target has keyboard focus, only that it is styled + * as such. + * @param {boolean} focused Whether to apply or remove styling to indicate that + * the component's element has keyboard focus. + */ +goog.ui.Control.prototype.setFocused = function(focused) { + if (this.isTransitionAllowed(goog.ui.Component.State.FOCUSED, focused)) { + this.setState(goog.ui.Component.State.FOCUSED, focused); + } +}; + + +/** + * Returns true if the component is open (expanded), false otherwise. + * @return {boolean} Whether the component is open. + */ +goog.ui.Control.prototype.isOpen = function() { + return this.hasState(goog.ui.Component.State.OPENED); +}; + + +/** + * Opens (expands) or closes (collapses) the component. Does nothing if this + * state transition is disallowed. + * @param {boolean} open Whether to open or close the component. + * @see #isTransitionAllowed + */ +goog.ui.Control.prototype.setOpen = function(open) { + if (this.isTransitionAllowed(goog.ui.Component.State.OPENED, open)) { + this.setState(goog.ui.Component.State.OPENED, open); + } +}; + + +/** + * Returns the component's state as a bit mask of {@link + * goog.ui.Component.State}s. + * @return {number} Bit mask representing component state. + */ +goog.ui.Control.prototype.getState = function() { + return this.state_; +}; + + +/** + * Returns true if the component is in the specified state, false otherwise. + * @param {goog.ui.Component.State} state State to check. + * @return {boolean} Whether the component is in the given state. + */ +goog.ui.Control.prototype.hasState = function(state) { + return !!(this.state_ & state); +}; + + +/** + * Sets or clears the given state on the component, and updates its styling + * accordingly. Does nothing if the component is already in the correct state + * or if it doesn't support the specified state. Doesn't dispatch any state + * transition events; use advisedly. + * @param {goog.ui.Component.State} state State to set or clear. + * @param {boolean} enable Whether to set or clear the state (if supported). + * @param {boolean=} opt_calledFrom Prevents looping with setEnabled. + */ +goog.ui.Control.prototype.setState = function(state, enable, opt_calledFrom) { + if (!opt_calledFrom && state == goog.ui.Component.State.DISABLED) { + this.setEnabled(!enable); + return; + } + if (this.isSupportedState(state) && enable != this.hasState(state)) { + // Delegate actual styling to the renderer, since it is DOM-specific. + this.renderer_.setState(this, state, enable); + this.state_ = enable ? this.state_ | state : this.state_ & ~state; + } +}; + + +/** + * Sets the component's state to the state represented by a bit mask of + * {@link goog.ui.Component.State}s. Unlike {@link #setState}, doesn't + * update the component's styling, and doesn't reject unsupported states. + * Called by renderers during element decoration. Considered protected; + * should only be used within this package and by subclasses. + * + * This should only be used by subclasses and its associated renderers. + * + * @param {number} state Bit mask representing component state. + */ +goog.ui.Control.prototype.setStateInternal = function(state) { + this.state_ = state; +}; + + +/** + * Returns true if the component supports the specified state, false otherwise. + * @param {goog.ui.Component.State} state State to check. + * @return {boolean} Whether the component supports the given state. + */ +goog.ui.Control.prototype.isSupportedState = function(state) { + return !!(this.supportedStates_ & state); +}; + + +/** + * Enables or disables support for the given state. Disabling support + * for a state while the component is in that state is an error. + * @param {goog.ui.Component.State} state State to support or de-support. + * @param {boolean} support Whether the component should support the state. + * @throws {Error} If disabling support for a state the control is currently in. + */ +goog.ui.Control.prototype.setSupportedState = function(state, support) { + if (this.isInDocument() && this.hasState(state) && !support) { + // Since we hook up event handlers in enterDocument(), this is an error. + throw new Error(goog.ui.Component.Error.ALREADY_RENDERED); + } + + if (!support && this.hasState(state)) { + // We are removing support for a state that the component is currently in. + this.setState(state, false); + } + + this.supportedStates_ = + support ? this.supportedStates_ | state : this.supportedStates_ & ~state; +}; + + +/** + * Returns true if the component provides default event handling for the state, + * false otherwise. + * @param {goog.ui.Component.State} state State to check. + * @return {boolean} Whether the component provides default event handling for + * the state. + */ +goog.ui.Control.prototype.isAutoState = function(state) { + return !!(this.autoStates_ & state) && this.isSupportedState(state); +}; + + +/** + * Enables or disables automatic event handling for the given state(s). + * @param {number} states Bit mask of {@link goog.ui.Component.State}s for which + * default event handling is to be enabled or disabled. + * @param {boolean} enable Whether the component should provide default event + * handling for the state(s). + */ +goog.ui.Control.prototype.setAutoStates = function(states, enable) { + this.autoStates_ = + enable ? this.autoStates_ | states : this.autoStates_ & ~states; +}; + + +/** + * Returns true if the component is set to dispatch transition events for the + * given state, false otherwise. + * @param {goog.ui.Component.State} state State to check. + * @return {boolean} Whether the component dispatches transition events for + * the state. + */ +goog.ui.Control.prototype.isDispatchTransitionEvents = function(state) { + return !!(this.statesWithTransitionEvents_ & state) && + this.isSupportedState(state); +}; + + +/** + * Enables or disables transition events for the given state(s). Controls + * handle state transitions internally by default, and only dispatch state + * transition events if explicitly requested to do so by calling this method. + * @param {number} states Bit mask of {@link goog.ui.Component.State}s for + * which transition events should be enabled or disabled. + * @param {boolean} enable Whether transition events should be enabled. + */ +goog.ui.Control.prototype.setDispatchTransitionEvents = function( + states, enable) { + this.statesWithTransitionEvents_ = enable ? + this.statesWithTransitionEvents_ | states : + this.statesWithTransitionEvents_ & ~states; +}; + + +/** + * Returns true if the transition into or out of the given state is allowed to + * proceed, false otherwise. A state transition is allowed under the following + * conditions: + *
        + *
      • the component supports the state, + *
      • the component isn't already in the target state, + *
      • either the component is configured not to dispatch events for this + * state transition, or a transition event was dispatched and wasn't + * canceled by any event listener, and + *
      • the component hasn't been disposed of + *
      + * Considered protected; should only be used within this package and by + * subclasses. + * @param {goog.ui.Component.State} state State to/from which the control is + * transitioning. + * @param {boolean} enable Whether the control is entering or leaving the state. + * @return {boolean} Whether the state transition is allowed to proceed. + * @protected + */ +goog.ui.Control.prototype.isTransitionAllowed = function(state, enable) { + return this.isSupportedState(state) && this.hasState(state) != enable && + (!(this.statesWithTransitionEvents_ & state) || + this.dispatchEvent( + goog.ui.Component.getStateTransitionEvent(state, enable))) && + !this.isDisposed(); +}; + + +// Default event handlers, to be overridden in subclasses. + + +/** + * Handles mouseover events. Dispatches an ENTER event; if the event isn't + * canceled, the component is enabled, and it supports auto-highlighting, + * highlights the component. Considered protected; should only be used + * within this package and by subclasses. + * @param {goog.events.BrowserEvent} e Mouse event to handle. + */ +goog.ui.Control.prototype.handleMouseOver = function(e) { + // Ignore mouse moves between descendants. + if (!goog.ui.Control.isMouseEventWithinElement_(e, this.getElement()) && + this.dispatchEvent(goog.ui.Component.EventType.ENTER) && + this.isEnabled() && this.isAutoState(goog.ui.Component.State.HOVER)) { + this.setHighlighted(true); + } +}; + + +/** + * Handles mouseout events. Dispatches a LEAVE event; if the event isn't + * canceled, and the component supports auto-highlighting, deactivates and + * un-highlights the component. Considered protected; should only be used + * within this package and by subclasses. + * @param {goog.events.BrowserEvent} e Mouse event to handle. + */ +goog.ui.Control.prototype.handleMouseOut = function(e) { + if (!goog.ui.Control.isMouseEventWithinElement_(e, this.getElement()) && + this.dispatchEvent(goog.ui.Component.EventType.LEAVE)) { + if (this.isAutoState(goog.ui.Component.State.ACTIVE)) { + // Deactivate on mouseout; otherwise we lose track of the mouse button. + this.setActive(false); + } + if (this.isAutoState(goog.ui.Component.State.HOVER)) { + this.setHighlighted(false); + } + } +}; + + +/** + * @param {!goog.events.BrowserEvent} e Event to handle. + * @private + */ +goog.ui.Control.prototype.preventPointerCapture_ = function(e) { + var elem = /** @type {!Element} */ (e.target); + if (!!elem.releasePointerCapture) { + elem.releasePointerCapture(e.pointerId); + } +}; + + +/** + * Handles contextmenu events. + * @param {goog.events.BrowserEvent} e Event to handle. + */ +goog.ui.Control.prototype.handleContextMenu = goog.nullFunction; + + +/** + * Checks if a mouse event (mouseover or mouseout) occurred below an element. + * @param {goog.events.BrowserEvent} e Mouse event (should be mouseover or + * mouseout). + * @param {Element} elem The ancestor element. + * @return {boolean} Whether the event has a relatedTarget (the element the + * mouse is coming from) and it's a descendant of elem. + * @private + */ +goog.ui.Control.isMouseEventWithinElement_ = function(e, elem) { + // If relatedTarget is null, it means there was no previous element (e.g. + // the mouse moved out of the window). Assume this means that the mouse + // event was not within the element. + return !!e.relatedTarget && goog.dom.contains(elem, e.relatedTarget); +}; + + +/** + * Handles mousedown events. If the component is enabled, highlights and + * activates it. If the component isn't configured for keyboard access, + * prevents it from receiving keyboard focus. Considered protected; should + * only be used within this package and by subclasses. + * @param {goog.events.Event} e Mouse event to handle. + */ +goog.ui.Control.prototype.handleMouseDown = function(e) { + if (this.isEnabled()) { + // Highlight enabled control on mousedown, regardless of the mouse button. + if (this.isAutoState(goog.ui.Component.State.HOVER)) { + this.setHighlighted(true); + } + + // For the left button only, activate the control, and focus its key event + // target (if supported). + if (e.isMouseActionButton()) { + if (this.isAutoState(goog.ui.Component.State.ACTIVE)) { + this.setActive(true); + } + if (this.renderer_ && this.renderer_.isFocusable(this)) { + this.getKeyEventTarget().focus(); + } + } + } + + // Cancel the default action unless the control allows text selection. + if (!this.isAllowTextSelection() && e.isMouseActionButton()) { + e.preventDefault(); + } +}; + + +/** + * Handles mouseup events. If the component is enabled, highlights it. If + * the component has previously been activated, performs its associated action + * by calling {@link performActionInternal}, then deactivates it. Considered + * protected; should only be used within this package and by subclasses. + * @param {goog.events.Event} e Mouse event to handle. + */ +goog.ui.Control.prototype.handleMouseUp = function(e) { + if (this.isEnabled()) { + if (this.isAutoState(goog.ui.Component.State.HOVER)) { + this.setHighlighted(true); + } + if (this.isActive() && this.performActionInternal(e) && + this.isAutoState(goog.ui.Component.State.ACTIVE)) { + this.setActive(false); + } + } +}; + + +/** + * Handles dblclick events. Should only be registered if the user agent is + * IE. If the component is enabled, performs its associated action by calling + * {@link performActionInternal}. This is used to allow more performant + * buttons in IE. In IE, no mousedown event is fired when that mousedown will + * trigger a dblclick event. Because of this, a user clicking quickly will + * only cause ACTION events to fire on every other click. This is a workaround + * to generate ACTION events for every click. Unfortunately, this workaround + * won't ever trigger the ACTIVE state. This is roughly the same behaviour as + * if this were a 'button' element with a listener on mouseup. Considered + * protected; should only be used within this package and by subclasses. + * @param {goog.events.Event} e Mouse event to handle. + */ +goog.ui.Control.prototype.handleDblClick = function(e) { + if (this.isEnabled()) { + this.performActionInternal(e); + } +}; + + +/** + * Performs the appropriate action when the control is activated by the user. + * The default implementation first updates the checked and selected state of + * controls that support them, then dispatches an ACTION event. Considered + * protected; should only be used within this package and by subclasses. + * @param {goog.events.Event} e Event that triggered the action. + * @return {boolean} Whether the action is allowed to proceed. + * @protected + */ +goog.ui.Control.prototype.performActionInternal = function(e) { + if (this.isAutoState(goog.ui.Component.State.CHECKED)) { + this.setChecked(!this.isChecked()); + } + if (this.isAutoState(goog.ui.Component.State.SELECTED)) { + this.setSelected(true); + } + if (this.isAutoState(goog.ui.Component.State.OPENED)) { + this.setOpen(!this.isOpen()); + } + + var actionEvent = + new goog.events.Event(goog.ui.Component.EventType.ACTION, this); + if (e) { + actionEvent.altKey = e.altKey; + actionEvent.ctrlKey = e.ctrlKey; + actionEvent.metaKey = e.metaKey; + actionEvent.shiftKey = e.shiftKey; + actionEvent.platformModifierKey = e.platformModifierKey; + } + return this.dispatchEvent(actionEvent); +}; + + +/** + * Handles focus events on the component's key event target element. If the + * component is focusable, updates its state and styling to indicate that it + * now has keyboard focus. Considered protected; should only be used within + * this package and by subclasses. Warning: IE dispatches focus and + * blur events asynchronously! + * @param {goog.events.Event} e Focus event to handle. + */ +goog.ui.Control.prototype.handleFocus = function(e) { + if (this.isAutoState(goog.ui.Component.State.FOCUSED)) { + this.setFocused(true); + } +}; + + +/** + * Handles blur events on the component's key event target element. Always + * deactivates the component. In addition, if the component is focusable, + * updates its state and styling to indicate that it no longer has keyboard + * focus. Considered protected; should only be used within this package and + * by subclasses. Warning: IE dispatches focus and blur events + * asynchronously! + * @param {goog.events.Event} e Blur event to handle. + */ +goog.ui.Control.prototype.handleBlur = function(e) { + if (this.isAutoState(goog.ui.Component.State.ACTIVE)) { + this.setActive(false); + } + if (this.isAutoState(goog.ui.Component.State.FOCUSED)) { + this.setFocused(false); + } +}; + + +/** + * Attempts to handle a keyboard event, if the component is enabled and visible, + * by calling {@link handleKeyEventInternal}. Considered protected; should only + * be used within this package and by subclasses. + * @param {goog.events.KeyEvent} e Key event to handle. + * @return {boolean} Whether the key event was handled. + */ +goog.ui.Control.prototype.handleKeyEvent = function(e) { + if (this.isVisible() && this.isEnabled() && this.handleKeyEventInternal(e)) { + e.preventDefault(); + e.stopPropagation(); + return true; + } + return false; +}; + + +/** + * Attempts to handle a keyboard event; returns true if the event was handled, + * false otherwise. Considered protected; should only be used within this + * package and by subclasses. + * @param {goog.events.KeyEvent} e Key event to handle. + * @return {boolean} Whether the key event was handled. + * @protected + */ +goog.ui.Control.prototype.handleKeyEventInternal = function(e) { + return e.keyCode == goog.events.KeyCodes.ENTER && + this.performActionInternal(e); +}; + + +// Register the default renderer for goog.ui.Controls. +goog.ui.registry.setDefaultRenderer(goog.ui.Control, goog.ui.ControlRenderer); + + +// Register a decorator factory function for goog.ui.Controls. +goog.ui.registry.setDecoratorByClassName( + goog.ui.ControlRenderer.CSS_CLASS, + function() { return new goog.ui.Control(null); }); + + + +/** + * A singleton that helps goog.ui.Control instances play well with screen + * readers. It necessitated by shortcomings in IE, and need not be + * instantiated in any other browser. + * + * In most cases, a click on a goog.ui.Control results in a sequence of events: + * MOUSEDOWN, MOUSEUP and CLICK. UI controls rely on this sequence since most + * behavior is trigged by MOUSEDOWN and MOUSEUP. But when IE is used with some + * traditional screen readers (JAWS, NVDA and perhaps others), IE only sends + * the CLICK event, resulting in the control being unresponsive. This class + * monitors the sequence of these events, and if it detects a CLICK event not + * not preceded by a MOUSEUP event, directly calls the control's event handlers + * for MOUSEDOWN, then MOUSEUP. While the resulting sequence is different from + * the norm (the CLICK comes first instead of last), testing thus far shows + * the resulting behavior to be correct. + * + * See http://goo.gl/qvQR4C for more details. + * + * @param {!goog.ui.Control} control + * @constructor + * @extends {goog.Disposable} + * @private + */ +goog.ui.Control.IeMouseEventSequenceSimulator_ = function(control) { + goog.ui.Control.IeMouseEventSequenceSimulator_.base(this, 'constructor'); + + /** @private {goog.ui.Control}*/ + this.control_ = control; + + /** @private {boolean} */ + this.clickExpected_ = false; + + /** @private @const {!goog.events.EventHandler< + * !goog.ui.Control.IeMouseEventSequenceSimulator_>} + */ + this.handler_ = new goog.events.EventHandler(this); + this.registerDisposable(this.handler_); + + var element = this.control_.getElementStrict(); + this.handler_ + .listen(element, goog.events.EventType.MOUSEDOWN, this.handleMouseDown_) + .listen(element, goog.events.EventType.MOUSEUP, this.handleMouseUp_) + .listen(element, goog.events.EventType.CLICK, this.handleClick_); +}; +goog.inherits(goog.ui.Control.IeMouseEventSequenceSimulator_, goog.Disposable); + + +/** + * Whether this browser supports synthetic MouseEvents. + * + * See https://msdn.microsoft.com/library/dn905219(v=vs.85).aspx for details. + * + * @private {boolean} + * @const + */ +goog.ui.Control.IeMouseEventSequenceSimulator_.SYNTHETIC_EVENTS_ = + !goog.userAgent.IE || goog.userAgent.isDocumentModeOrHigher(9); + + +/** @private */ +goog.ui.Control.IeMouseEventSequenceSimulator_.prototype.handleMouseDown_ = + function() { + this.clickExpected_ = false; +}; + + +/** @private */ +goog.ui.Control.IeMouseEventSequenceSimulator_.prototype.handleMouseUp_ = + function() { + this.clickExpected_ = true; +}; + + +/** + * @param {!MouseEvent} e + * @param {goog.events.EventType} typeArg + * @return {!MouseEvent} + * @private + */ +goog.ui.Control.IeMouseEventSequenceSimulator_.makeLeftMouseEvent_ = function( + e, typeArg) { + 'use strict'; + + if (!goog.ui.Control.IeMouseEventSequenceSimulator_.SYNTHETIC_EVENTS_) { + // IE < 9 does not support synthetic mouse events. Therefore, reuse the + // existing MouseEvent by overwriting the read only button and type + // properties. As IE < 9 does not support ES5 strict mode this will not + // generate an exception even when the script specifies "use strict". + e.button = goog.events.BrowserEvent.MouseButton.LEFT; + e.type = typeArg; + return e; + } + + var event = /** @type {!MouseEvent} */ (document.createEvent('MouseEvents')); + event.initMouseEvent( + typeArg, e.bubbles, e.cancelable, + e.view || null, // IE9 errors if view is undefined + e.detail, e.screenX, e.screenY, e.clientX, e.clientY, e.ctrlKey, e.altKey, + e.shiftKey, e.metaKey, goog.events.BrowserEvent.MouseButton.LEFT, + e.relatedTarget || null); // IE9 errors if relatedTarget is undefined + return event; +}; + + +/** + * @param {!goog.events.Event} e + * @private + */ +goog.ui.Control.IeMouseEventSequenceSimulator_.prototype.handleClick_ = + function(e) { + if (this.clickExpected_) { + // This is the end of a normal click sequence: mouse-down, mouse-up, click. + // Assume appropriate actions have already been performed. + this.clickExpected_ = false; + return; + } + + // For click events not part of a normal sequence, similate the mouse-down and + // mouse-up events by creating synthetic events for each and directly invoke + // the corresponding event listeners in order. + + var browserEvent = /** @type {goog.events.BrowserEvent} */ (e); + + var event = /** @type {!MouseEvent} */ (browserEvent.getBrowserEvent()); + var origEventButton = event.button; + var origEventType = event.type; + + var down = goog.ui.Control.IeMouseEventSequenceSimulator_.makeLeftMouseEvent_( + event, goog.events.EventType.MOUSEDOWN); + this.control_.handleMouseDown( + new goog.events.BrowserEvent(down, browserEvent.currentTarget)); + + var up = goog.ui.Control.IeMouseEventSequenceSimulator_.makeLeftMouseEvent_( + event, goog.events.EventType.MOUSEUP); + this.control_.handleMouseUp( + new goog.events.BrowserEvent(up, browserEvent.currentTarget)); + + if (goog.ui.Control.IeMouseEventSequenceSimulator_.SYNTHETIC_EVENTS_) { + // This browser supports synthetic events. Avoid resetting the read only + // properties (type, button) as they were not overwritten and writing them + // results in an exception when running in ES5 strict mode. + return; + } + + // Restore original values for click handlers that have not yet been invoked. + event.button = origEventButton; + event.type = origEventType; +}; + + +/** @override */ +goog.ui.Control.IeMouseEventSequenceSimulator_.prototype.disposeInternal = + function() { + this.control_ = null; + goog.ui.Control.IeMouseEventSequenceSimulator_.base(this, 'disposeInternal'); +}; diff --git a/closure-library/closure/goog/ui/controlcontent.js b/closure-library/closure/goog/ui/controlcontent.js new file mode 100644 index 0000000000..38615bef3f --- /dev/null +++ b/closure-library/closure/goog/ui/controlcontent.js @@ -0,0 +1,28 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Type declaration for control content. + * + * @author nicksantos@google.com (Nick Santos) + */ +goog.provide('goog.ui.ControlContent'); + + +/** + * Type declaration for text caption or DOM structure to be used as the content + * of {@link goog.ui.Control}s. + * @typedef {string|Node|Array|NodeList} + */ +goog.ui.ControlContent; diff --git a/closure-library/closure/goog/ui/controlrenderer.js b/closure-library/closure/goog/ui/controlrenderer.js new file mode 100644 index 0000000000..ff404d1f6f --- /dev/null +++ b/closure-library/closure/goog/ui/controlrenderer.js @@ -0,0 +1,953 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Base class for control renderers. + * TODO(attila): If the renderer framework works well, pull it into Component. + * + * @author attila@google.com (Attila Bodis) + */ + +goog.provide('goog.ui.ControlRenderer'); + +goog.require('goog.a11y.aria'); +goog.require('goog.a11y.aria.Role'); +goog.require('goog.a11y.aria.State'); +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.classlist'); +goog.require('goog.object'); +goog.require('goog.string'); +goog.require('goog.style'); +goog.require('goog.ui.Component'); +goog.require('goog.ui.ControlContent'); +goog.require('goog.userAgent'); + +goog.forwardDeclare('goog.ui.Control'); // circular + + + +/** + * Default renderer for {@link goog.ui.Control}s. Can be used as-is, but + * subclasses of Control will probably want to use renderers specifically + * tailored for them by extending this class. Controls that use renderers + * delegate one or more of the following API methods to the renderer: + *
        + *
      • `createDom` - renders the DOM for the component + *
      • `canDecorate` - determines whether an element can be decorated + * by the component + *
      • `decorate` - decorates an existing element with the component + *
      • `setState` - updates the appearance of the component based on + * its state + *
      • `getContent` - returns the component's content + *
      • `setContent` - sets the component's content + *
      + * Controls are stateful; renderers, on the other hand, should be stateless and + * reusable. + * @constructor + */ +goog.ui.ControlRenderer = function() {}; +goog.addSingletonGetter(goog.ui.ControlRenderer); +goog.tagUnsealableClass(goog.ui.ControlRenderer); + + +/** + * Constructs a new renderer and sets the CSS class that the renderer will use + * as the base CSS class to apply to all elements rendered by that renderer. + * An example to use this function using a color palette: + * + *
      + * var myCustomRenderer = goog.ui.ControlRenderer.getCustomRenderer(
      + *     goog.ui.PaletteRenderer, 'my-special-palette');
      + * var newColorPalette = new goog.ui.ColorPalette(
      + *     colors, myCustomRenderer, opt_domHelper);
      + * 
      + * + * Your CSS can look like this now: + *
      + * .my-special-palette { }
      + * .my-special-palette-table { }
      + * .my-special-palette-cell { }
      + * etc.
      + * 
      + * + * instead of + *
      + * .CSS_MY_SPECIAL_PALETTE .goog-palette { }
      + * .CSS_MY_SPECIAL_PALETTE .goog-palette-table { }
      + * .CSS_MY_SPECIAL_PALETTE .goog-palette-cell { }
      + * etc.
      + * 
      + * + * You would want to use this functionality when you want an instance of a + * component to have specific styles different than the other components of the + * same type in your application. This avoids using descendant selectors to + * apply the specific styles to this component. + * + * @param {Function} ctor The constructor of the renderer you are trying to + * create. + * @param {string} cssClassName The name of the CSS class for this renderer. + * @return {goog.ui.ControlRenderer} An instance of the desired renderer with + * its getCssClass() method overridden to return the supplied custom CSS + * class name. + */ +goog.ui.ControlRenderer.getCustomRenderer = function(ctor, cssClassName) { + var renderer = new ctor(); + + /** + * Returns the CSS class to be applied to the root element of components + * rendered using this renderer. + * @return {string} Renderer-specific CSS class. + */ + renderer.getCssClass = function() { return cssClassName; }; + + return renderer; +}; + + +/** + * Default CSS class to be applied to the root element of components rendered + * by this renderer. + * @type {string} + */ +goog.ui.ControlRenderer.CSS_CLASS = goog.getCssName('goog-control'); + + +/** + * Array of arrays of CSS classes that we want composite classes added and + * removed for in IE6 and lower as a workaround for lack of multi-class CSS + * selector support. + * + * Subclasses that have accompanying CSS requiring this workaround should define + * their own static IE6_CLASS_COMBINATIONS constant and override + * getIe6ClassCombinations to return it. + * + * For example, if your stylesheet uses the selector .button.collapse-left + * (and is compiled to .button_collapse-left for the IE6 version of the + * stylesheet,) you should include ['button', 'collapse-left'] in this array + * and the class button_collapse-left will be applied to the root element + * whenever both button and collapse-left are applied individually. + * + * Members of each class name combination will be joined with underscores in the + * order that they're defined in the array. You should alphabetize them (for + * compatibility with the CSS compiler) unless you are doing something special. + * @type {Array>} + */ +goog.ui.ControlRenderer.IE6_CLASS_COMBINATIONS = []; + + +/** + * Map of component states to corresponding ARIA attributes. Since the mapping + * of component states to ARIA attributes is neither component- nor + * renderer-specific, this is a static property of the renderer class, and is + * initialized on first use. + * @type {Object} + * @private + */ +goog.ui.ControlRenderer.ariaAttributeMap_; + + +/** + * Map of certain ARIA states to ARIA roles that support them. Used for checked + * and selected Component states because they are used on Components with ARIA + * roles that do not support the corresponding ARIA state. + * @private {!Object} + * @const + */ +goog.ui.ControlRenderer.TOGGLE_ARIA_STATE_MAP_ = goog.object.create( + goog.a11y.aria.Role.BUTTON, goog.a11y.aria.State.PRESSED, + goog.a11y.aria.Role.CHECKBOX, goog.a11y.aria.State.CHECKED, + goog.a11y.aria.Role.MENU_ITEM, goog.a11y.aria.State.SELECTED, + goog.a11y.aria.Role.MENU_ITEM_CHECKBOX, goog.a11y.aria.State.CHECKED, + goog.a11y.aria.Role.MENU_ITEM_RADIO, goog.a11y.aria.State.CHECKED, + goog.a11y.aria.Role.RADIO, goog.a11y.aria.State.CHECKED, + goog.a11y.aria.Role.TAB, goog.a11y.aria.State.SELECTED, + goog.a11y.aria.Role.TREEITEM, goog.a11y.aria.State.SELECTED); + + +/** + * Returns the ARIA role to be applied to the control. + * See http://wiki/Main/ARIA for more info. + * @return {goog.a11y.aria.Role|undefined} ARIA role. + */ +goog.ui.ControlRenderer.prototype.getAriaRole = function() { + // By default, the ARIA role is unspecified. + return undefined; +}; + + +/** + * Returns the control's contents wrapped in a DIV, with the renderer's own + * CSS class and additional state-specific classes applied to it. + * @param {goog.ui.Control} control Control to render. + * @return {Element} Root element for the control. + */ +goog.ui.ControlRenderer.prototype.createDom = function(control) { + // Create and return DIV wrapping contents. + var element = control.getDomHelper().createDom( + goog.dom.TagName.DIV, this.getClassNames(control).join(' '), + control.getContent()); + + return element; +}; + + +/** + * Takes the control's root element and returns the parent element of the + * control's contents. Since by default controls are rendered as a single + * DIV, the default implementation returns the element itself. Subclasses + * with more complex DOM structures must override this method as needed. + * @param {Element} element Root element of the control whose content element + * is to be returned. + * @return {Element} The control's content element. + */ +goog.ui.ControlRenderer.prototype.getContentElement = function(element) { + return element; +}; + + +/** + * Updates the control's DOM by adding or removing the specified class name + * to/from its root element. May add additional combined classes as needed in + * IE6 and lower. Because of this, subclasses should use this method when + * modifying class names on the control's root element. + * @param {goog.ui.Control|Element} control Control instance (or root element) + * to be updated. + * @param {string} className CSS class name to add or remove. + * @param {boolean} enable Whether to add or remove the class name. + */ +goog.ui.ControlRenderer.prototype.enableClassName = function( + control, className, enable) { + var element = /** @type {Element} */ ( + control.getElement ? control.getElement() : control); + if (element) { + var classNames = [className]; + + // For IE6, we need to enable any combined classes involving this class + // as well. + // TODO(user): Remove this as IE6 is no longer in use. + if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('7')) { + classNames = this.getAppliedCombinedClassNames_( + goog.dom.classlist.get(element), className); + classNames.push(className); + } + + goog.dom.classlist.enableAll(element, classNames, enable); + } +}; + + +/** + * Updates the control's DOM by adding or removing the specified extra class + * name to/from its element. + * @param {goog.ui.Control} control Control to be updated. + * @param {string} className CSS class name to add or remove. + * @param {boolean} enable Whether to add or remove the class name. + */ +goog.ui.ControlRenderer.prototype.enableExtraClassName = function( + control, className, enable) { + // The base class implementation is trivial; subclasses should override as + // needed. + this.enableClassName(control, className, enable); +}; + + +/** + * Returns true if this renderer can decorate the element, false otherwise. + * The default implementation always returns true. + * @param {Element} element Element to decorate. + * @return {boolean} Whether the renderer can decorate the element. + */ +goog.ui.ControlRenderer.prototype.canDecorate = function(element) { + return true; +}; + + +/** + * Default implementation of `decorate` for {@link goog.ui.Control}s. + * Initializes the control's ID, content, and state based on the ID of the + * element, its child nodes, and its CSS classes, respectively. Returns the + * element. + * @param {goog.ui.Control} control Control instance to decorate the element. + * @param {Element} element Element to decorate. + * @return {Element} Decorated element. + */ +goog.ui.ControlRenderer.prototype.decorate = function(control, element) { + // Set the control's ID to the decorated element's DOM ID, if any. + if (element.id) { + control.setId(element.id); + } + + // Set the control's content to the decorated element's content. + var contentElem = this.getContentElement(element); + if (contentElem && contentElem.firstChild) { + control.setContentInternal( + contentElem.firstChild.nextSibling ? + goog.array.clone(contentElem.childNodes) : + contentElem.firstChild); + } else { + control.setContentInternal(null); + } + + // Initialize the control's state based on the decorated element's CSS class. + // This implementation is optimized to minimize object allocations, string + // comparisons, and DOM access. + var state = 0x00; + var rendererClassName = this.getCssClass(); + var structuralClassName = this.getStructuralCssClass(); + var hasRendererClassName = false; + var hasStructuralClassName = false; + var hasCombinedClassName = false; + var classNames = goog.array.toArray(goog.dom.classlist.get(element)); + goog.array.forEach(classNames, function(className) { + if (!hasRendererClassName && className == rendererClassName) { + hasRendererClassName = true; + if (structuralClassName == rendererClassName) { + hasStructuralClassName = true; + } + } else if (!hasStructuralClassName && className == structuralClassName) { + hasStructuralClassName = true; + } else { + state |= this.getStateFromClass(className); + } + if (this.getStateFromClass(className) == goog.ui.Component.State.DISABLED) { + goog.asserts.assertElement(contentElem); + if (goog.dom.isFocusableTabIndex(contentElem)) { + goog.dom.setFocusableTabIndex(contentElem, false); + } + } + }, this); + control.setStateInternal(state); + + // Make sure the element has the renderer's CSS classes applied, as well as + // any extra class names set on the control. + if (!hasRendererClassName) { + classNames.push(rendererClassName); + if (structuralClassName == rendererClassName) { + hasStructuralClassName = true; + } + } + if (!hasStructuralClassName) { + classNames.push(structuralClassName); + } + var extraClassNames = control.getExtraClassNames(); + if (extraClassNames) { + classNames.push.apply(classNames, extraClassNames); + } + + // For IE6, rewrite all classes on the decorated element if any combined + // classes apply. + if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('7')) { + var combinedClasses = this.getAppliedCombinedClassNames_(classNames); + if (combinedClasses.length > 0) { + classNames.push.apply(classNames, combinedClasses); + hasCombinedClassName = true; + } + } + + // Only write to the DOM if new class names had to be added to the element. + if (!hasRendererClassName || !hasStructuralClassName || extraClassNames || + hasCombinedClassName) { + goog.dom.classlist.set(element, classNames.join(' ')); + } + + return element; +}; + + +/** + * Initializes the control's DOM by configuring properties that can only be set + * after the DOM has entered the document. This implementation sets up BiDi + * and keyboard focus. Called from {@link goog.ui.Control#enterDocument}. + * @param {goog.ui.Control} control Control whose DOM is to be initialized + * as it enters the document. + */ +goog.ui.ControlRenderer.prototype.initializeDom = function(control) { + // Initialize render direction (BiDi). We optimize the left-to-right render + // direction by assuming that elements are left-to-right by default, and only + // updating their styling if they are explicitly set to right-to-left. + if (control.isRightToLeft()) { + this.setRightToLeft(control.getElement(), true); + } + + // Initialize keyboard focusability (tab index). We assume that components + // aren't focusable by default (i.e have no tab index), and only touch the + // DOM if the component is focusable, enabled, and visible, and therefore + // needs a tab index. + if (control.isEnabled()) { + this.setFocusable(control, control.isVisible()); + } +}; + + +/** + * Sets the element's ARIA role. + * @param {Element} element Element to update. + * @param {?goog.a11y.aria.Role=} opt_preferredRole The preferred ARIA role. + */ +goog.ui.ControlRenderer.prototype.setAriaRole = function( + element, opt_preferredRole) { + var ariaRole = opt_preferredRole || this.getAriaRole(); + if (ariaRole) { + goog.asserts.assert( + element, 'The element passed as a first parameter cannot be null.'); + var currentRole = goog.a11y.aria.getRole(element); + if (ariaRole == currentRole) { + return; + } + goog.a11y.aria.setRole(element, ariaRole); + } +}; + + +/** + * Sets the element's ARIA attributes, including distinguishing between + * universally supported ARIA properties and ARIA states that are only + * supported by certain ARIA roles. Only attributes which are initialized to be + * true will be set. + * @param {!goog.ui.Control} control Control whose ARIA state will be updated. + * @param {!Element} element Element whose ARIA state is to be updated. + */ +goog.ui.ControlRenderer.prototype.setAriaStates = function(control, element) { + goog.asserts.assert(control); + goog.asserts.assert(element); + + var ariaLabel = control.getAriaLabel(); + if (goog.isDefAndNotNull(ariaLabel)) { + this.setAriaLabel(element, ariaLabel); + } + + if (!control.isVisible()) { + goog.a11y.aria.setState( + element, goog.a11y.aria.State.HIDDEN, !control.isVisible()); + } + if (!control.isEnabled()) { + this.updateAriaState( + element, goog.ui.Component.State.DISABLED, !control.isEnabled()); + } + if (control.isSupportedState(goog.ui.Component.State.SELECTED)) { + this.updateAriaState( + element, goog.ui.Component.State.SELECTED, control.isSelected()); + } + if (control.isSupportedState(goog.ui.Component.State.CHECKED)) { + this.updateAriaState( + element, goog.ui.Component.State.CHECKED, control.isChecked()); + } + if (control.isSupportedState(goog.ui.Component.State.OPENED)) { + this.updateAriaState( + element, goog.ui.Component.State.OPENED, control.isOpen()); + } +}; + + +/** + * Sets the element's ARIA label. This should be overriden by subclasses that + * don't apply the role directly on control.element_. + * @param {!Element} element Element whose ARIA label is to be updated. + * @param {string} ariaLabel Label to add to the element. + */ +goog.ui.ControlRenderer.prototype.setAriaLabel = function(element, ariaLabel) { + goog.a11y.aria.setLabel(element, ariaLabel); +}; + + +/** + * Allows or disallows text selection within the control's DOM. + * @param {Element} element The control's root element. + * @param {boolean} allow Whether the element should allow text selection. + */ +goog.ui.ControlRenderer.prototype.setAllowTextSelection = function( + element, allow) { + // On all browsers other than IE and Opera, it isn't necessary to recursively + // apply unselectable styling to the element's children. + goog.style.setUnselectable( + element, !allow, !goog.userAgent.IE && !goog.userAgent.OPERA); +}; + + +/** + * Applies special styling to/from the control's element if it is rendered + * right-to-left, and removes it if it is rendered left-to-right. + * @param {Element} element The control's root element. + * @param {boolean} rightToLeft Whether the component is rendered + * right-to-left. + */ +goog.ui.ControlRenderer.prototype.setRightToLeft = function( + element, rightToLeft) { + this.enableClassName( + element, goog.getCssName(this.getStructuralCssClass(), 'rtl'), + rightToLeft); +}; + + +/** + * Returns true if the control's key event target supports keyboard focus + * (based on its `tabIndex` attribute), false otherwise. + * @param {goog.ui.Control} control Control whose key event target is to be + * checked. + * @return {boolean} Whether the control's key event target is focusable. + */ +goog.ui.ControlRenderer.prototype.isFocusable = function(control) { + var keyTarget; + if (control.isSupportedState(goog.ui.Component.State.FOCUSED) && + (keyTarget = control.getKeyEventTarget())) { + return goog.dom.isFocusableTabIndex(keyTarget); + } + return false; +}; + + +/** + * Updates the control's key event target to make it focusable or non-focusable + * via its `tabIndex` attribute. Does nothing if the control doesn't + * support the `FOCUSED` state, or if it has no key event target. + * @param {goog.ui.Control} control Control whose key event target is to be + * updated. + * @param {boolean} focusable Whether to enable keyboard focus support on the + * control's key event target. + */ +goog.ui.ControlRenderer.prototype.setFocusable = function(control, focusable) { + var keyTarget; + if (control.isSupportedState(goog.ui.Component.State.FOCUSED) && + (keyTarget = control.getKeyEventTarget())) { + if (!focusable && control.isFocused()) { + // Blur before hiding. Note that IE calls onblur handlers asynchronously. + try { + keyTarget.blur(); + } catch (e) { + // TODO(user|user): Find out why this fails on IE. + } + // The blur event dispatched by the key event target element when blur() + // was called on it should have been handled by the control's handleBlur() + // method, so at this point the control should no longer be focused. + // However, blur events are unreliable on IE and FF3, so if at this point + // the control is still focused, we trigger its handleBlur() method + // programmatically. + if (control.isFocused()) { + control.handleBlur(null); + } + } + // Don't overwrite existing tab index values unless needed. + if (goog.dom.isFocusableTabIndex(keyTarget) != focusable) { + goog.dom.setFocusableTabIndex(keyTarget, focusable); + } + } +}; + + +/** + * Shows or hides the element. + * @param {Element} element Element to update. + * @param {boolean} visible Whether to show the element. + */ +goog.ui.ControlRenderer.prototype.setVisible = function(element, visible) { + // The base class implementation is trivial; subclasses should override as + // needed. It should be possible to do animated reveals, for example. + goog.style.setElementShown(element, visible); + if (element) { + goog.a11y.aria.setState(element, goog.a11y.aria.State.HIDDEN, !visible); + } +}; + + +/** + * Updates the appearance of the control in response to a state change. + * @param {goog.ui.Control} control Control instance to update. + * @param {goog.ui.Component.State} state State to enable or disable. + * @param {boolean} enable Whether the control is entering or exiting the state. + */ +goog.ui.ControlRenderer.prototype.setState = function(control, state, enable) { + var element = control.getElement(); + if (element) { + var className = this.getClassForState(state); + if (className) { + this.enableClassName(control, className, enable); + } + this.updateAriaState(element, state, enable); + } +}; + + +/** + * Updates the element's ARIA (accessibility) attributes , including + * distinguishing between universally supported ARIA properties and ARIA states + * that are only supported by certain ARIA roles. + * @param {Element} element Element whose ARIA state is to be updated. + * @param {goog.ui.Component.State} state Component state being enabled or + * disabled. + * @param {boolean} enable Whether the state is being enabled or disabled. + * @protected + */ +goog.ui.ControlRenderer.prototype.updateAriaState = function( + element, state, enable) { + // Ensure the ARIA attribute map exists. + if (!goog.ui.ControlRenderer.ariaAttributeMap_) { + goog.ui.ControlRenderer.ariaAttributeMap_ = goog.object.create( + goog.ui.Component.State.DISABLED, goog.a11y.aria.State.DISABLED, + goog.ui.Component.State.SELECTED, goog.a11y.aria.State.SELECTED, + goog.ui.Component.State.CHECKED, goog.a11y.aria.State.CHECKED, + goog.ui.Component.State.OPENED, goog.a11y.aria.State.EXPANDED); + } + goog.asserts.assert( + element, 'The element passed as a first parameter cannot be null.'); + var ariaAttr = goog.ui.ControlRenderer.getAriaStateForAriaRole_( + element, goog.ui.ControlRenderer.ariaAttributeMap_[state]); + if (ariaAttr) { + goog.a11y.aria.setState(element, ariaAttr, enable); + } +}; + + +/** + * Returns the appropriate ARIA attribute based on ARIA role if the ARIA + * attribute is an ARIA state. + * @param {!Element} element The element from which to get the ARIA role for + * matching ARIA state. + * @param {goog.a11y.aria.State} attr The ARIA attribute to check to see if it + * can be applied to the given ARIA role. + * @return {goog.a11y.aria.State} An ARIA attribute that can be applied to the + * given ARIA role. + * @private + */ +goog.ui.ControlRenderer.getAriaStateForAriaRole_ = function(element, attr) { + var role = goog.a11y.aria.getRole(element); + if (!role) { + return attr; + } + role = /** @type {goog.a11y.aria.Role} */ (role); + var matchAttr = goog.ui.ControlRenderer.TOGGLE_ARIA_STATE_MAP_[role] || attr; + return goog.ui.ControlRenderer.isAriaState_(attr) ? matchAttr : attr; +}; + + +/** + * Determines if the given ARIA attribute is an ARIA property or ARIA state. + * @param {goog.a11y.aria.State} attr The ARIA attribute to classify. + * @return {boolean} If the ARIA attribute is an ARIA state. + * @private + */ +goog.ui.ControlRenderer.isAriaState_ = function(attr) { + return attr == goog.a11y.aria.State.CHECKED || + attr == goog.a11y.aria.State.SELECTED; +}; + + +/** + * Takes a control's root element, and sets its content to the given text + * caption or DOM structure. The default implementation replaces the children + * of the given element. Renderers that create more complex DOM structures + * must override this method accordingly. + * @param {Element} element The control's root element. + * @param {goog.ui.ControlContent} content Text caption or DOM structure to be + * set as the control's content. The DOM nodes will not be cloned, they + * will only moved under the content element of the control. + */ +goog.ui.ControlRenderer.prototype.setContent = function(element, content) { + var contentElem = this.getContentElement(element); + if (contentElem) { + goog.dom.removeChildren(contentElem); + if (content) { + if (goog.isString(content)) { + goog.dom.setTextContent(contentElem, content); + } else { + var childHandler = function(child) { + if (child) { + var doc = goog.dom.getOwnerDocument(contentElem); + contentElem.appendChild( + goog.isString(child) ? doc.createTextNode(child) : child); + } + }; + if (goog.isArray(content)) { + // Array of nodes. + goog.array.forEach(content, childHandler); + } else if (goog.isArrayLike(content) && !('nodeType' in content)) { + // NodeList. The second condition filters out TextNode which also has + // length attribute but is not array like. The nodes have to be cloned + // because childHandler removes them from the list during iteration. + goog.array.forEach( + goog.array.clone(/** @type {!NodeList} */ (content)), + childHandler); + } else { + // Node or string. + childHandler(content); + } + } + } + } +}; + + +/** + * Returns the element within the component's DOM that should receive keyboard + * focus (null if none). The default implementation returns the control's root + * element. + * @param {goog.ui.Control} control Control whose key event target is to be + * returned. + * @return {Element} The key event target. + */ +goog.ui.ControlRenderer.prototype.getKeyEventTarget = function(control) { + return control.getElement(); +}; + + +// CSS class name management. + + +/** + * Returns the CSS class name to be applied to the root element of all + * components rendered or decorated using this renderer. The class name + * is expected to uniquely identify the renderer class, i.e. no two + * renderer classes are expected to share the same CSS class name. + * @return {string} Renderer-specific CSS class name. + */ +goog.ui.ControlRenderer.prototype.getCssClass = function() { + return goog.ui.ControlRenderer.CSS_CLASS; +}; + + +/** + * Returns an array of combinations of classes to apply combined class names for + * in IE6 and below. See {@link IE6_CLASS_COMBINATIONS} for more detail. This + * method doesn't reference {@link IE6_CLASS_COMBINATIONS} so that it can be + * compiled out, but subclasses should return their IE6_CLASS_COMBINATIONS + * static constant instead. + * @return {Array>} Array of class name combinations. + */ +goog.ui.ControlRenderer.prototype.getIe6ClassCombinations = function() { + return []; +}; + + +/** + * Returns the name of a DOM structure-specific CSS class to be applied to the + * root element of all components rendered or decorated using this renderer. + * Unlike the class name returned by {@link #getCssClass}, the structural class + * name may be shared among different renderers that generate similar DOM + * structures. The structural class name also serves as the basis of derived + * class names used to identify and style structural elements of the control's + * DOM, as well as the basis for state-specific class names. The default + * implementation returns the same class name as {@link #getCssClass}, but + * subclasses are expected to override this method as needed. + * @return {string} DOM structure-specific CSS class name (same as the renderer- + * specific CSS class name by default). + */ +goog.ui.ControlRenderer.prototype.getStructuralCssClass = function() { + return this.getCssClass(); +}; + + +/** + * Returns all CSS class names applicable to the given control, based on its + * state. The return value is an array of strings containing + *
        + *
      1. the renderer-specific CSS class returned by {@link #getCssClass}, + * followed by + *
      2. the structural CSS class returned by {@link getStructuralCssClass} (if + * different from the renderer-specific CSS class), followed by + *
      3. any state-specific classes returned by {@link #getClassNamesForState}, + * followed by + *
      4. any extra classes returned by the control's `getExtraClassNames` + * method and + *
      5. for IE6 and lower, additional combined classes from + * {@link getAppliedCombinedClassNames_}. + *
      + * Since all controls have at least one renderer-specific CSS class name, this + * method is guaranteed to return an array of at least one element. + * @param {goog.ui.Control} control Control whose CSS classes are to be + * returned. + * @return {!Array} Array of CSS class names applicable to the control. + * @protected + */ +goog.ui.ControlRenderer.prototype.getClassNames = function(control) { + var cssClass = this.getCssClass(); + + // Start with the renderer-specific class name. + var classNames = [cssClass]; + + // Add structural class name, if different. + var structuralCssClass = this.getStructuralCssClass(); + if (structuralCssClass != cssClass) { + classNames.push(structuralCssClass); + } + + // Add state-specific class names, if any. + var classNamesForState = this.getClassNamesForState(control.getState()); + classNames.push.apply(classNames, classNamesForState); + + // Add extra class names, if any. + var extraClassNames = control.getExtraClassNames(); + if (extraClassNames) { + classNames.push.apply(classNames, extraClassNames); + } + + // Add composite classes for IE6 support + if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('7')) { + classNames.push.apply( + classNames, this.getAppliedCombinedClassNames_(classNames)); + } + + return classNames; +}; + + +/** + * Returns an array of all the combined class names that should be applied based + * on the given list of classes. Checks the result of + * {@link getIe6ClassCombinations} for any combinations that have all + * members contained in classes. If a combination matches, the members are + * joined with an underscore (in order), and added to the return array. + * + * If opt_includedClass is provided, return only the combined classes that have + * all members contained in classes AND include opt_includedClass as well. + * opt_includedClass is added to classes as well. + * @param {IArrayLike} classes Array-like thing of classes to + * return matching combined classes for. + * @param {?string=} opt_includedClass If provided, get only the combined + * classes that include this one. + * @return {!Array} Array of combined class names that should be + * applied. + * @private + */ +goog.ui.ControlRenderer.prototype.getAppliedCombinedClassNames_ = function( + classes, opt_includedClass) { + var toAdd = []; + if (opt_includedClass) { + classes = goog.array.concat(classes, [opt_includedClass]); + } + goog.array.forEach(this.getIe6ClassCombinations(), function(combo) { + if (goog.array.every(combo, goog.partial(goog.array.contains, classes)) && + (!opt_includedClass || goog.array.contains(combo, opt_includedClass))) { + toAdd.push(combo.join('_')); + } + }); + return toAdd; +}; + + +/** + * Takes a bit mask of {@link goog.ui.Component.State}s, and returns an array + * of the appropriate class names representing the given state, suitable to be + * applied to the root element of a component rendered using this renderer, or + * null if no state-specific classes need to be applied. This default + * implementation uses the renderer's {@link getClassForState} method to + * generate each state-specific class. + * @param {number} state Bit mask of component states. + * @return {!Array} Array of CSS class names representing the given + * state. + * @protected + */ +goog.ui.ControlRenderer.prototype.getClassNamesForState = function(state) { + var classNames = []; + while (state) { + // For each enabled state, push the corresponding CSS class name onto + // the classNames array. + var mask = state & -state; // Least significant bit + classNames.push( + this.getClassForState( + /** @type {goog.ui.Component.State} */ (mask))); + state &= ~mask; + } + return classNames; +}; + + +/** + * Takes a single {@link goog.ui.Component.State}, and returns the + * corresponding CSS class name (null if none). + * @param {goog.ui.Component.State} state Component state. + * @return {string|undefined} CSS class representing the given state (undefined + * if none). + * @protected + */ +goog.ui.ControlRenderer.prototype.getClassForState = function(state) { + if (!this.classByState_) { + this.createClassByStateMap_(); + } + return this.classByState_[state]; +}; + + +/** + * Takes a single CSS class name which may represent a component state, and + * returns the corresponding component state (0x00 if none). + * @param {string} className CSS class name, possibly representing a component + * state. + * @return {goog.ui.Component.State} state Component state corresponding + * to the given CSS class (0x00 if none). + * @protected + */ +goog.ui.ControlRenderer.prototype.getStateFromClass = function(className) { + if (!this.stateByClass_) { + this.createStateByClassMap_(); + } + var state = parseInt(this.stateByClass_[className], 10); + return /** @type {goog.ui.Component.State} */ (isNaN(state) ? 0x00 : state); +}; + + +/** + * Creates the lookup table of states to classes, used during state changes. + * @private + */ +goog.ui.ControlRenderer.prototype.createClassByStateMap_ = function() { + var baseClass = this.getStructuralCssClass(); + + // This ensures space-separated css classnames are not allowed, which some + // ControlRenderers had been doing. See http://b/13694665. + var isValidClassName = + !goog.string.contains(goog.string.normalizeWhitespace(baseClass), ' '); + goog.asserts.assert( + isValidClassName, + 'ControlRenderer has an invalid css class: \'' + baseClass + '\''); + + /** + * Map of component states to state-specific structural class names, + * used when changing the DOM in response to a state change. Precomputed + * and cached on first use to minimize object allocations and string + * concatenation. + * @type {Object} + * @private + */ + this.classByState_ = goog.object.create( + goog.ui.Component.State.DISABLED, goog.getCssName(baseClass, 'disabled'), + goog.ui.Component.State.HOVER, goog.getCssName(baseClass, 'hover'), + goog.ui.Component.State.ACTIVE, goog.getCssName(baseClass, 'active'), + goog.ui.Component.State.SELECTED, goog.getCssName(baseClass, 'selected'), + goog.ui.Component.State.CHECKED, goog.getCssName(baseClass, 'checked'), + goog.ui.Component.State.FOCUSED, goog.getCssName(baseClass, 'focused'), + goog.ui.Component.State.OPENED, goog.getCssName(baseClass, 'open')); +}; + + +/** + * Creates the lookup table of classes to states, used during decoration. + * @private + */ +goog.ui.ControlRenderer.prototype.createStateByClassMap_ = function() { + // We need the classByState_ map so we can transpose it. + if (!this.classByState_) { + this.createClassByStateMap_(); + } + + /** + * Map of state-specific structural class names to component states, + * used during element decoration. Precomputed and cached on first use + * to minimize object allocations and string concatenation. + * @type {Object} + * @private + */ + this.stateByClass_ = goog.object.transpose(this.classByState_); +}; diff --git a/closure-library/closure/goog/ui/cookieeditor.js b/closure-library/closure/goog/ui/cookieeditor.js new file mode 100644 index 0000000000..c5b0275773 --- /dev/null +++ b/closure-library/closure/goog/ui/cookieeditor.js @@ -0,0 +1,181 @@ +// Copyright 2010 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Displays and edits the value of a cookie. + * Intended only for debugging. + */ +goog.provide('goog.ui.CookieEditor'); + +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.events.EventType'); +goog.require('goog.net.cookies'); +goog.require('goog.string'); +goog.require('goog.style'); +goog.require('goog.ui.Component'); + + + +/** + * Displays and edits the value of a cookie. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper. + * @constructor + * @extends {goog.ui.Component} + * @final + */ +goog.ui.CookieEditor = function(opt_domHelper) { + goog.ui.CookieEditor.base(this, 'constructor', opt_domHelper); +}; +goog.inherits(goog.ui.CookieEditor, goog.ui.Component); + + +/** + * Cookie key. + * @type {?string} + * @private + */ +goog.ui.CookieEditor.prototype.cookieKey_; + + +/** + * Text area. + * @type {HTMLTextAreaElement} + * @private + */ +goog.ui.CookieEditor.prototype.textAreaElem_; + + +/** + * Clear button. + * @type {HTMLButtonElement} + * @private + */ +goog.ui.CookieEditor.prototype.clearButtonElem_; + + +/** + * Invalid value warning text. + * @type {HTMLSpanElement} + * @private + */ +goog.ui.CookieEditor.prototype.valueWarningElem_; + + +/** + * Update button. + * @type {HTMLButtonElement} + * @private + */ +goog.ui.CookieEditor.prototype.updateButtonElem_; + + +// TODO(user): add combobox for user to select different cookies +/** + * Sets the cookie which this component will edit. + * @param {string} cookieKey Cookie key. + */ +goog.ui.CookieEditor.prototype.selectCookie = function(cookieKey) { + goog.asserts.assert(goog.net.cookies.isValidName(cookieKey)); + this.cookieKey_ = cookieKey; + if (this.textAreaElem_) { + this.textAreaElem_.value = goog.net.cookies.get(cookieKey) || ''; + } +}; + + +/** @override */ +goog.ui.CookieEditor.prototype.canDecorate = function() { + return false; +}; + + +/** @override */ +goog.ui.CookieEditor.prototype.createDom = function() { + // Debug-only, so we don't need i18n. + this.clearButtonElem_ = goog.dom.createDom( + goog.dom.TagName.BUTTON, /* attributes */ null, 'Clear'); + this.updateButtonElem_ = goog.dom.createDom( + goog.dom.TagName.BUTTON, /* attributes */ null, 'Update'); + var value = this.cookieKey_ && goog.net.cookies.get(this.cookieKey_); + this.textAreaElem_ = goog.dom.createDom( + goog.dom.TagName.TEXTAREA, /* attibutes */ null, value || ''); + this.valueWarningElem_ = goog.dom.createDom( + goog.dom.TagName.SPAN, + /* attibutes */ {'style': 'display:none;color:red'}, + 'Invalid cookie value.'); + this.setElementInternal( + goog.dom.createDom( + goog.dom.TagName.DIV, + /* attibutes */ null, this.valueWarningElem_, + goog.dom.createDom(goog.dom.TagName.BR), this.textAreaElem_, + goog.dom.createDom(goog.dom.TagName.BR), this.clearButtonElem_, + this.updateButtonElem_)); +}; + + +/** @override */ +goog.ui.CookieEditor.prototype.enterDocument = function() { + goog.ui.CookieEditor.base(this, 'enterDocument'); + this.getHandler().listen( + this.clearButtonElem_, goog.events.EventType.CLICK, this.handleClear_); + this.getHandler().listen( + this.updateButtonElem_, goog.events.EventType.CLICK, this.handleUpdate_); +}; + + +/** + * Handles user clicking clear button. + * @param {!goog.events.Event} e The click event. + * @private + */ +goog.ui.CookieEditor.prototype.handleClear_ = function(e) { + if (this.cookieKey_) { + goog.net.cookies.remove(this.cookieKey_); + } + this.textAreaElem_.value = ''; +}; + + +/** + * Handles user clicking update button. + * @param {!goog.events.Event} e The click event. + * @private + */ +goog.ui.CookieEditor.prototype.handleUpdate_ = function(e) { + if (this.cookieKey_) { + var value = this.textAreaElem_.value; + if (value) { + // Strip line breaks. + value = goog.string.stripNewlines(value); + } + if (goog.net.cookies.isValidValue(value)) { + goog.net.cookies.set(this.cookieKey_, value); + goog.style.setElementShown(this.valueWarningElem_, false); + } else { + goog.style.setElementShown(this.valueWarningElem_, true); + } + } +}; + + +/** @override */ +goog.ui.CookieEditor.prototype.disposeInternal = function() { + this.clearButtonElem_ = null; + this.cookieKey_ = null; + this.textAreaElem_ = null; + this.updateButtonElem_ = null; + this.valueWarningElem_ = null; +}; diff --git a/closure-library/closure/goog/ui/css3buttonrenderer.js b/closure-library/closure/goog/ui/css3buttonrenderer.js new file mode 100644 index 0000000000..942550deda --- /dev/null +++ b/closure-library/closure/goog/ui/css3buttonrenderer.js @@ -0,0 +1,146 @@ +// Copyright 2010 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview An alternative imageless button renderer that uses CSS3 rather + * than voodoo to render custom buttons with rounded corners and dimensionality + * (via a subtle flat shadow on the bottom half of the button) without the use + * of images. + * + * Based on the Custom Buttons 3.1 visual specification, see + * http://go/custombuttons + * + * Tested and verified to work in Gecko 1.9.2+ and WebKit 528+. + * + * @author eae@google.com (Emil A Eklund) + * @see ../demos/css3button.html + */ + +goog.provide('goog.ui.Css3ButtonRenderer'); + +goog.require('goog.asserts'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.classlist'); +goog.require('goog.ui.Button'); +goog.require('goog.ui.ButtonRenderer'); +goog.require('goog.ui.Component'); +goog.require('goog.ui.INLINE_BLOCK_CLASSNAME'); +goog.require('goog.ui.registry'); + + + +/** + * Custom renderer for {@link goog.ui.Button}s. Css3 buttons can contain + * almost arbitrary HTML content, will flow like inline elements, but can be + * styled like block-level elements. + * + * @constructor + * @extends {goog.ui.ButtonRenderer} + * @final + */ +goog.ui.Css3ButtonRenderer = function() { + goog.ui.ButtonRenderer.call(this); +}; +goog.inherits(goog.ui.Css3ButtonRenderer, goog.ui.ButtonRenderer); +goog.addSingletonGetter(goog.ui.Css3ButtonRenderer); + + +/** + * Default CSS class to be applied to the root element of components rendered + * by this renderer. + * @type {string} + */ +goog.ui.Css3ButtonRenderer.CSS_CLASS = goog.getCssName('goog-css3-button'); + + +/** @override */ +goog.ui.Css3ButtonRenderer.prototype.getContentElement = function(element) { + return /** @type {Element} */ (element); +}; + + +/** + * Returns the button's contents wrapped in the following DOM structure: + * + *
      + * Contents... + *
      + * + * Overrides {@link goog.ui.ButtonRenderer#createDom}. + * @param {goog.ui.Control} control goog.ui.Button to render. + * @return {!Element} Root element for the button. + * @override + */ +goog.ui.Css3ButtonRenderer.prototype.createDom = function(control) { + var button = /** @type {goog.ui.Button} */ (control); + var classNames = this.getClassNames(button); + return button.getDomHelper().createDom( + goog.dom.TagName.DIV, { + 'class': goog.ui.INLINE_BLOCK_CLASSNAME + ' ' + classNames.join(' '), + 'title': button.getTooltip() || '' + }, + button.getContent()); +}; + + +/** + * Returns true if this renderer can decorate the element. Overrides + * {@link goog.ui.ButtonRenderer#canDecorate} by returning true if the + * element is a DIV, false otherwise. + * @param {Element} element Element to decorate. + * @return {boolean} Whether the renderer can decorate the element. + * @override + */ +goog.ui.Css3ButtonRenderer.prototype.canDecorate = function(element) { + return element.tagName == goog.dom.TagName.DIV; +}; + + +/** @override */ +goog.ui.Css3ButtonRenderer.prototype.decorate = function(button, element) { + goog.asserts.assert(element); + goog.dom.classlist.addAll( + element, [goog.ui.INLINE_BLOCK_CLASSNAME, this.getCssClass()]); + return goog.ui.Css3ButtonRenderer.superClass_.decorate.call( + this, button, element); +}; + + +/** + * Returns the CSS class to be applied to the root element of components + * rendered using this renderer. + * @return {string} Renderer-specific CSS class. + * @override + */ +goog.ui.Css3ButtonRenderer.prototype.getCssClass = function() { + return goog.ui.Css3ButtonRenderer.CSS_CLASS; +}; + + +// Register a decorator factory function for goog.ui.Css3ButtonRenderer. +goog.ui.registry.setDecoratorByClassName( + goog.ui.Css3ButtonRenderer.CSS_CLASS, function() { + return new goog.ui.Button(null, goog.ui.Css3ButtonRenderer.getInstance()); + }); + + +// Register a decorator factory function for toggle buttons using the +// goog.ui.Css3ButtonRenderer. +goog.ui.registry.setDecoratorByClassName( + goog.getCssName('goog-css3-toggle-button'), function() { + var button = + new goog.ui.Button(null, goog.ui.Css3ButtonRenderer.getInstance()); + button.setSupportedState(goog.ui.Component.State.CHECKED, true); + return button; + }); diff --git a/closure-library/closure/goog/ui/css3menubuttonrenderer.js b/closure-library/closure/goog/ui/css3menubuttonrenderer.js new file mode 100644 index 0000000000..672f5cad16 --- /dev/null +++ b/closure-library/closure/goog/ui/css3menubuttonrenderer.js @@ -0,0 +1,144 @@ +// Copyright 2010 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview An alternative imageless button renderer that uses CSS3 rather + * than voodoo to render custom buttons with rounded corners and dimensionality + * (via a subtle flat shadow on the bottom half of the button) without the use + * of images. + * + * Based on the Custom Buttons 3.1 visual specification, see + * http://go/custombuttons + * + * Tested and verified to work in Gecko 1.9.2+ and WebKit 528+. + * + * @author eae@google.com (Emil A Eklund) + * @see ../demos/css3menubutton.html + */ + +goog.provide('goog.ui.Css3MenuButtonRenderer'); + +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.ui.INLINE_BLOCK_CLASSNAME'); +goog.require('goog.ui.MenuButton'); +goog.require('goog.ui.MenuButtonRenderer'); +goog.require('goog.ui.registry'); + + + +/** + * Custom renderer for {@link goog.ui.MenuButton}s. Css3 buttons can contain + * almost arbitrary HTML content, will flow like inline elements, but can be + * styled like block-level elements. + * + * @constructor + * @extends {goog.ui.MenuButtonRenderer} + * @final + */ +goog.ui.Css3MenuButtonRenderer = function() { + goog.ui.MenuButtonRenderer.call(this); +}; +goog.inherits(goog.ui.Css3MenuButtonRenderer, goog.ui.MenuButtonRenderer); +goog.addSingletonGetter(goog.ui.Css3MenuButtonRenderer); + + +/** + * Default CSS class to be applied to the root element of components rendered + * by this renderer. + * @type {string} + */ +goog.ui.Css3MenuButtonRenderer.CSS_CLASS = goog.getCssName('goog-css3-button'); + + +/** @override */ +goog.ui.Css3MenuButtonRenderer.prototype.getContentElement = function(element) { + if (element) { + var captionElem = goog.dom.getElementsByTagNameAndClass( + '*', goog.getCssName(this.getCssClass(), 'caption'), element)[0]; + return captionElem; + } + return null; +}; + + +/** + * Returns true if this renderer can decorate the element. Overrides + * {@link goog.ui.MenuButtonRenderer#canDecorate} by returning true if the + * element is a DIV, false otherwise. + * @param {Element} element Element to decorate. + * @return {boolean} Whether the renderer can decorate the element. + * @override + */ +goog.ui.Css3MenuButtonRenderer.prototype.canDecorate = function(element) { + return element.tagName == goog.dom.TagName.DIV; +}; + + +/** + * Takes a text caption or existing DOM structure, and returns the content + * wrapped in a pseudo-rounded-corner box. Creates the following DOM structure: + * + *
      + *
      Contents...
      + *
      + *
      + * + * Used by both {@link #createDom} and {@link #decorate}. To be overridden + * by subclasses. + * @param {goog.ui.ControlContent} content Text caption or DOM structure to wrap + * in a box. + * @param {goog.dom.DomHelper} dom DOM helper, used for document interaction. + * @return {!Element} Pseudo-rounded-corner box containing the content. + * @override + */ +goog.ui.Css3MenuButtonRenderer.prototype.createButton = function(content, dom) { + var baseClass = this.getCssClass(); + var inlineBlock = goog.ui.INLINE_BLOCK_CLASSNAME + ' '; + return dom.createDom( + goog.dom.TagName.DIV, inlineBlock, + dom.createDom( + goog.dom.TagName.DIV, + [ + goog.getCssName(baseClass, 'caption'), + goog.getCssName('goog-inline-block') + ], + content), + dom.createDom(goog.dom.TagName.DIV, [ + goog.getCssName(baseClass, 'dropdown'), + goog.getCssName('goog-inline-block') + ])); +}; + + +/** + * Returns the CSS class to be applied to the root element of components + * rendered using this renderer. + * @return {string} Renderer-specific CSS class. + * @override + */ +goog.ui.Css3MenuButtonRenderer.prototype.getCssClass = function() { + return goog.ui.Css3MenuButtonRenderer.CSS_CLASS; +}; + + +// Register a decorator factory function for goog.ui.Css3MenuButtonRenderer. +// Since we're using goog-css3-button as the base class in order to get the +// same styling as goog.ui.Css3ButtonRenderer, we need to be explicit about +// giving goog-css3-menu-button here. +goog.ui.registry.setDecoratorByClassName( + goog.getCssName('goog-css3-menu-button'), function() { + return new goog.ui.MenuButton( + null, null, goog.ui.Css3MenuButtonRenderer.getInstance()); + }); diff --git a/closure-library/closure/goog/ui/cssnames.js b/closure-library/closure/goog/ui/cssnames.js new file mode 100644 index 0000000000..4ad5419ba5 --- /dev/null +++ b/closure-library/closure/goog/ui/cssnames.js @@ -0,0 +1,29 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Common CSS class name constants. + * + * @author mkretzschmar@google.com (Martin Kretzschmar) + */ + +goog.provide('goog.ui.INLINE_BLOCK_CLASSNAME'); + + +/** + * CSS class name for applying the "display: inline-block" property in a + * cross-browser way. + * @type {string} + */ +goog.ui.INLINE_BLOCK_CLASSNAME = goog.getCssName('goog-inline-block'); diff --git a/closure-library/closure/goog/ui/custombutton.js b/closure-library/closure/goog/ui/custombutton.js new file mode 100644 index 0000000000..d7a91648eb --- /dev/null +++ b/closure-library/closure/goog/ui/custombutton.js @@ -0,0 +1,59 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A button rendered via {@link goog.ui.CustomButtonRenderer}. + * + * @author attila@google.com (Attila Bodis) + */ + +goog.provide('goog.ui.CustomButton'); + +goog.require('goog.ui.Button'); +goog.require('goog.ui.CustomButtonRenderer'); +goog.require('goog.ui.registry'); + + + +/** + * A custom button control. Identical to {@link goog.ui.Button}, except it + * defaults its renderer to {@link goog.ui.CustomButtonRenderer}. One could + * just as easily pass `goog.ui.CustomButtonRenderer.getInstance()` to + * the {@link goog.ui.Button} constructor and get the same result. Provided + * for convenience. + * + * @param {goog.ui.ControlContent} content Text caption or existing DOM + * structure to display as the button's caption. + * @param {goog.ui.ButtonRenderer=} opt_renderer Optional renderer used to + * render or decorate the button; defaults to + * {@link goog.ui.CustomButtonRenderer}. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for + * document interaction. + * @constructor + * @extends {goog.ui.Button} + */ +goog.ui.CustomButton = function(content, opt_renderer, opt_domHelper) { + goog.ui.Button.call( + this, content, opt_renderer || goog.ui.CustomButtonRenderer.getInstance(), + opt_domHelper); +}; +goog.inherits(goog.ui.CustomButton, goog.ui.Button); + + +// Register a decorator factory function for goog.ui.CustomButtons. +goog.ui.registry.setDecoratorByClassName( + goog.ui.CustomButtonRenderer.CSS_CLASS, function() { + // CustomButton defaults to using CustomButtonRenderer. + return new goog.ui.CustomButton(null); + }); diff --git a/closure-library/closure/goog/ui/custombuttonrenderer.js b/closure-library/closure/goog/ui/custombuttonrenderer.js new file mode 100644 index 0000000000..53be5693fe --- /dev/null +++ b/closure-library/closure/goog/ui/custombuttonrenderer.js @@ -0,0 +1,277 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A custom button renderer that uses CSS voodoo to render a + * button-like object with fake rounded corners. + * + * @author attila@google.com (Attila Bodis) + */ + +goog.provide('goog.ui.CustomButtonRenderer'); + +goog.require('goog.a11y.aria.Role'); +goog.require('goog.asserts'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.classlist'); +goog.require('goog.string'); +goog.require('goog.ui.ButtonRenderer'); +goog.require('goog.ui.INLINE_BLOCK_CLASSNAME'); + + + +/** + * Custom renderer for {@link goog.ui.Button}s. Custom buttons can contain + * almost arbitrary HTML content, will flow like inline elements, but can be + * styled like block-level elements. + * + * @constructor + * @extends {goog.ui.ButtonRenderer} + */ +goog.ui.CustomButtonRenderer = function() { + goog.ui.ButtonRenderer.call(this); +}; +goog.inherits(goog.ui.CustomButtonRenderer, goog.ui.ButtonRenderer); +goog.addSingletonGetter(goog.ui.CustomButtonRenderer); + + +/** + * Default CSS class to be applied to the root element of components rendered + * by this renderer. + * @type {string} + */ +goog.ui.CustomButtonRenderer.CSS_CLASS = goog.getCssName('goog-custom-button'); + + +/** + * Returns the button's contents wrapped in the following DOM structure: + * + *
      + *
      + *
      + * Contents... + *
      + *
      + *
      + * + * Overrides {@link goog.ui.ButtonRenderer#createDom}. + * @param {goog.ui.Control} control goog.ui.Button to render. + * @return {!Element} Root element for the button. + * @override + */ +goog.ui.CustomButtonRenderer.prototype.createDom = function(control) { + var button = /** @type {goog.ui.Button} */ (control); + var classNames = this.getClassNames(button); + var buttonElement = button.getDomHelper().createDom( + goog.dom.TagName.DIV, + goog.ui.INLINE_BLOCK_CLASSNAME + ' ' + classNames.join(' '), + this.createButton(button.getContent(), button.getDomHelper())); + this.setTooltip(buttonElement, /** @type {string}*/ (button.getTooltip())); + + return buttonElement; +}; + + +/** + * Returns the ARIA role to be applied to custom buttons. + * @return {goog.a11y.aria.Role|undefined} ARIA role. + * @override + */ +goog.ui.CustomButtonRenderer.prototype.getAriaRole = function() { + return goog.a11y.aria.Role.BUTTON; +}; + + +/** + * Takes the button's root element and returns the parent element of the + * button's contents. Overrides the superclass implementation by taking + * the nested DIV structure of custom buttons into account. + * @param {Element} element Root element of the button whose content + * element is to be returned. + * @return {Element} The button's content element (if any). + * @override + */ +goog.ui.CustomButtonRenderer.prototype.getContentElement = function(element) { + return element && element.firstChild && + /** @type {Element} */ (element.firstChild.firstChild); +}; + + +/** + * Takes a text caption or existing DOM structure, and returns the content + * wrapped in a pseudo-rounded-corner box. Creates the following DOM structure: + * + *
      + *
      + * Contents... + *
      + *
      + * + * Used by both {@link #createDom} and {@link #decorate}. To be overridden + * by subclasses. + * @param {goog.ui.ControlContent} content Text caption or DOM structure to wrap + * in a box. + * @param {goog.dom.DomHelper} dom DOM helper, used for document interaction. + * @return {Element} Pseudo-rounded-corner box containing the content. + */ +goog.ui.CustomButtonRenderer.prototype.createButton = function(content, dom) { + return dom.createDom( + goog.dom.TagName.DIV, goog.ui.INLINE_BLOCK_CLASSNAME + ' ' + + goog.getCssName(this.getCssClass(), 'outer-box'), + dom.createDom( + goog.dom.TagName.DIV, goog.ui.INLINE_BLOCK_CLASSNAME + ' ' + + goog.getCssName(this.getCssClass(), 'inner-box'), + content)); +}; + + +/** + * Returns true if this renderer can decorate the element. Overrides + * {@link goog.ui.ButtonRenderer#canDecorate} by returning true if the + * element is a DIV, false otherwise. + * @param {Element} element Element to decorate. + * @return {boolean} Whether the renderer can decorate the element. + * @override + */ +goog.ui.CustomButtonRenderer.prototype.canDecorate = function(element) { + return element.tagName == goog.dom.TagName.DIV; +}; + + +/** + * Check if the button's element has a box structure. + * @param {goog.ui.Button} button Button instance whose structure is being + * checked. + * @param {Element} element Element of the button. + * @return {boolean} Whether the element has a box structure. + * @protected + */ +goog.ui.CustomButtonRenderer.prototype.hasBoxStructure = function( + button, element) { + var outer = button.getDomHelper().getFirstElementChild(element); + var outerClassName = goog.getCssName(this.getCssClass(), 'outer-box'); + if (outer && goog.dom.classlist.contains(outer, outerClassName)) { + var inner = button.getDomHelper().getFirstElementChild(outer); + var innerClassName = goog.getCssName(this.getCssClass(), 'inner-box'); + if (inner && goog.dom.classlist.contains(inner, innerClassName)) { + // We have a proper box structure. + return true; + } + } + return false; +}; + + +/** + * Takes an existing element and decorates it with the custom button control. + * Initializes the control's ID, content, tooltip, value, and state based + * on the ID of the element, its child nodes, and its CSS classes, respectively. + * Returns the element. Overrides {@link goog.ui.ButtonRenderer#decorate}. + * @param {goog.ui.Control} control Button instance to decorate the element. + * @param {Element} element Element to decorate. + * @return {Element} Decorated element. + * @override + */ +goog.ui.CustomButtonRenderer.prototype.decorate = function(control, element) { + goog.asserts.assert(element); + + var button = /** @type {goog.ui.Button} */ (control); + // Trim text nodes in the element's child node list; otherwise madness + // ensues (i.e. on Gecko, buttons will flicker and shift when moused over). + goog.ui.CustomButtonRenderer.trimTextNodes_(element, true); + goog.ui.CustomButtonRenderer.trimTextNodes_(element, false); + + // Create the buttom dom if it has not been created. + if (!this.hasBoxStructure(button, element)) { + element.appendChild( + this.createButton(element.childNodes, button.getDomHelper())); + } + + goog.dom.classlist.addAll( + element, [goog.ui.INLINE_BLOCK_CLASSNAME, this.getCssClass()]); + return goog.ui.CustomButtonRenderer.superClass_.decorate.call( + this, button, element); +}; + + +/** + * Returns the CSS class to be applied to the root element of components + * rendered using this renderer. + * @return {string} Renderer-specific CSS class. + * @override + */ +goog.ui.CustomButtonRenderer.prototype.getCssClass = function() { + return goog.ui.CustomButtonRenderer.CSS_CLASS; +}; + + +/** + * Takes an element and removes leading or trailing whitespace from the start + * or the end of its list of child nodes. The Boolean argument determines + * whether to trim from the start or the end of the node list. Empty text + * nodes are removed, and the first non-empty text node is trimmed from the + * left or the right as appropriate. For example, + * + *
      + * #text "" + * #text "\n Hello " + * ... + * #text " World! \n" + * #text "" + *
      + * + * becomes + * + *
      + * #text "Hello " + * ... + * #text " World!" + *
      + * + * This is essential for Gecko, where leading/trailing whitespace messes with + * the layout of elements with -moz-inline-box (used in goog-inline-block), and + * optional but harmless for non-Gecko. + * + * @param {Element} element Element whose child node list is to be trimmed. + * @param {boolean} fromStart Whether to trim from the start or from the end. + * @private + */ +goog.ui.CustomButtonRenderer.trimTextNodes_ = function(element, fromStart) { + if (element) { + var node = fromStart ? element.firstChild : element.lastChild, next; + // Tag soup HTML may result in a DOM where siblings have different parents. + while (node && node.parentNode == element) { + // Get the next/previous sibling here, since the node may be removed. + next = fromStart ? node.nextSibling : node.previousSibling; + if (node.nodeType == goog.dom.NodeType.TEXT) { + // Found a text node. + var text = node.nodeValue; + if (goog.string.trim(text) == '') { + // Found an empty text node; remove it. + element.removeChild(node); + } else { + // Found a non-empty text node; trim from the start/end, then exit. + node.nodeValue = fromStart ? goog.string.trimLeft(text) : + goog.string.trimRight(text); + break; + } + } else { + // Found a non-text node; done. + break; + } + node = next; + } + } +}; diff --git a/closure-library/closure/goog/ui/customcolorpalette.js b/closure-library/closure/goog/ui/customcolorpalette.js new file mode 100644 index 0000000000..c752144c2b --- /dev/null +++ b/closure-library/closure/goog/ui/customcolorpalette.js @@ -0,0 +1,143 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A color palette with a button for adding additional colors + * manually. + * + */ + +goog.provide('goog.ui.CustomColorPalette'); + +goog.require('goog.color'); +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.classlist'); +goog.require('goog.ui.ColorPalette'); +goog.require('goog.ui.Component'); + + + +/** + * A custom color palette is a grid of color swatches and a button that allows + * the user to add additional colors to the palette + * + * @param {Array} initColors Array of initial colors to populate the + * palette with. + * @param {goog.ui.PaletteRenderer=} opt_renderer Renderer used to render or + * decorate the palette; defaults to {@link goog.ui.PaletteRenderer}. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for + * document interaction. + * @constructor + * @extends {goog.ui.ColorPalette} + * @final + */ +goog.ui.CustomColorPalette = function(initColors, opt_renderer, opt_domHelper) { + goog.ui.ColorPalette.call(this, initColors, opt_renderer, opt_domHelper); + this.setSupportedState(goog.ui.Component.State.OPENED, true); +}; +goog.inherits(goog.ui.CustomColorPalette, goog.ui.ColorPalette); + + +/** + * Returns an array of DOM nodes for each color, and an additional cell with a + * '+'. + * @override + */ +goog.ui.CustomColorPalette.prototype.createColorNodes = function() { + /** @desc Hover caption for the button that allows the user to add a color. */ + var MSG_CLOSURE_CUSTOM_COLOR_BUTTON = goog.getMsg('Add a color'); + + var nl = goog.ui.CustomColorPalette.base(this, 'createColorNodes'); + nl.push( + goog.dom.createDom( + goog.dom.TagName.DIV, { + 'class': goog.getCssName('goog-palette-customcolor'), + 'title': MSG_CLOSURE_CUSTOM_COLOR_BUTTON + }, + '+')); + return nl; +}; + + +/** + * @override + * @param {goog.events.Event} e Mouse or key event that triggered the action. + * @return {boolean} True if the action was allowed to proceed, false otherwise. + */ +goog.ui.CustomColorPalette.prototype.performActionInternal = function(e) { + var item = /** @type {Element} */ (this.getHighlightedItem()); + if (item) { + if (goog.dom.classlist.contains( + item, goog.getCssName('goog-palette-customcolor'))) { + // User activated the special "add custom color" swatch. + this.promptForCustomColor(); + } else { + // User activated a normal color swatch. + this.setSelectedItem(item); + return this.dispatchEvent(goog.ui.Component.EventType.ACTION); + } + } + return false; +}; + + +/** + * Prompts the user to enter a custom color. Currently uses a window.prompt + * but could be updated to use a dialog box with a WheelColorPalette. + */ +goog.ui.CustomColorPalette.prototype.promptForCustomColor = function() { + /** @desc Default custom color dialog. */ + var MSG_CLOSURE_CUSTOM_COLOR_PROMPT = goog.getMsg( + 'Input custom color, i.e. pink, #F00, #D015FF or rgb(100, 50, 25)'); + + // A CustomColorPalette is considered "open" while the color selection prompt + // is open. Enabling state transition events for the OPENED state and + // listening for OPEN events allows clients to save the selection before + // it is destroyed (see e.g. bug 1064701). + var response = null; + this.setOpen(true); + if (this.isOpen()) { + // The OPEN event wasn't canceled; prompt for custom color. + response = window.prompt(MSG_CLOSURE_CUSTOM_COLOR_PROMPT, '#FFFFFF'); + this.setOpen(false); + } + + if (!response) { + // The user hit cancel + return; + } + + var color; + + try { + color = goog.color.parse(response).hex; + } catch (er) { + /** @desc Alert message sent when the input string is not a valid color. */ + var MSG_CLOSURE_CUSTOM_COLOR_INVALID_INPUT = goog.getMsg( + 'ERROR: "{$color}" is not a valid color.', {'color': response}); + alert(MSG_CLOSURE_CUSTOM_COLOR_INVALID_INPUT); + return; + } + + // TODO(user): This is relatively inefficient. Consider adding + // functionality to palette to add individual items after render time. + var colors = this.getColors(); + colors.push(color); + this.setColors(colors); + + // Set the selected color to the new color and notify listeners of the action. + this.setSelectedColor(color); + this.dispatchEvent(goog.ui.Component.EventType.ACTION); +}; diff --git a/closure-library/closure/goog/ui/datepicker.js b/closure-library/closure/goog/ui/datepicker.js new file mode 100644 index 0000000000..e51c594726 --- /dev/null +++ b/closure-library/closure/goog/ui/datepicker.js @@ -0,0 +1,1672 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Date picker implementation. + * + * @author eae@google.com (Emil A Eklund) + * @see ../demos/datepicker.html + */ + +goog.provide('goog.ui.DatePicker'); +goog.provide('goog.ui.DatePicker.Events'); +goog.provide('goog.ui.DatePickerEvent'); + +goog.require('goog.a11y.aria'); +goog.require('goog.asserts'); +goog.require('goog.date.Date'); +goog.require('goog.date.DateRange'); +goog.require('goog.date.Interval'); +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.classlist'); +goog.require('goog.events.Event'); +goog.require('goog.events.EventType'); +goog.require('goog.events.KeyHandler'); +goog.require('goog.i18n.DateTimeFormat'); +goog.require('goog.i18n.DateTimePatterns'); +goog.require('goog.i18n.DateTimeSymbols'); +goog.require('goog.style'); +goog.require('goog.ui.Component'); +goog.require('goog.ui.DefaultDatePickerRenderer'); +goog.require('goog.ui.IdGenerator'); + + + +/** + * DatePicker widget. Allows a single date to be selected from a calendar like + * view. + * + * @param {goog.date.Date|Date=} opt_date Date to initialize the date picker + * with, defaults to the current date. + * @param {Object=} opt_dateTimeSymbols Date and time symbols to use. + * Defaults to goog.i18n.DateTimeSymbols if not set. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper. + * @param {goog.ui.DatePickerRenderer=} opt_renderer Optional Date picker + * renderer. + * @constructor + * @extends {goog.ui.Component} + */ +goog.ui.DatePicker = function( + opt_date, opt_dateTimeSymbols, opt_domHelper, opt_renderer) { + goog.ui.Component.call(this, opt_domHelper); + + /** + * Date and time symbols to use. + * @type {!goog.i18n.DateTimeSymbolsType} + * @private + */ + this.symbols_ = /** @type {!goog.i18n.DateTimeSymbolsType} */ ( + opt_dateTimeSymbols || goog.i18n.DateTimeSymbols); + + this.wdayNames_ = this.symbols_.STANDALONESHORTWEEKDAYS; + + // Formatters for the various areas of the picker + this.i18nDateFormatterDay_ = new goog.i18n.DateTimeFormat('d', this.symbols_); + this.i18nDateFormatterDay2_ = + new goog.i18n.DateTimeFormat('dd', this.symbols_); + this.i18nDateFormatterWeek_ = + new goog.i18n.DateTimeFormat('w', this.symbols_); + // Formatter for day grid aria label. This should always have the day first so + // that a screenreader user can rapidly navigate within a month without always + // hearing the month. It should read the month name instead of number to avoid + // confusing people who are used to different orders. + this.i18nDateFormatterDayAriaLabel_ = + new goog.i18n.DateTimeFormat('d MMM', this.symbols_); + + // Previous implementation did not use goog.i18n.DateTimePatterns, + // so it is likely most developers did not set it. + // This is why the fallback to a hard-coded string (just in case). + var patYear = goog.i18n.DateTimePatterns.YEAR_FULL || 'y'; + this.i18nDateFormatterYear_ = + new goog.i18n.DateTimeFormat(patYear, this.symbols_); + var patMMMMy = goog.i18n.DateTimePatterns.YEAR_MONTH_FULL || 'MMMM y'; + this.i18nDateFormatterMonthYear_ = + new goog.i18n.DateTimeFormat(patMMMMy, this.symbols_); + + /** + * @type {!goog.ui.DatePickerRenderer} + * @private + */ + this.renderer_ = opt_renderer || + new goog.ui.DefaultDatePickerRenderer( + this.getBaseCssClass(), this.getDomHelper()); + + /** + * Selected date. + * @type {goog.date.Date} + * @private + */ + this.date_ = new goog.date.Date(opt_date); + this.date_.setFirstWeekCutOffDay(this.symbols_.FIRSTWEEKCUTOFFDAY); + this.date_.setFirstDayOfWeek(this.symbols_.FIRSTDAYOFWEEK); + + /** + * Active month. + * @type {goog.date.Date} + * @private + */ + this.activeMonth_ = this.date_.clone(); + this.activeMonth_.setDate(1); + + /** + * Class names to apply to the weekday columns. + * @type {Array} + * @private + */ + this.wdayStyles_ = ['', '', '', '', '', '', '']; + this.wdayStyles_[this.symbols_.WEEKENDRANGE[0]] = + goog.getCssName(this.getBaseCssClass(), 'wkend-start'); + this.wdayStyles_[this.symbols_.WEEKENDRANGE[1]] = + goog.getCssName(this.getBaseCssClass(), 'wkend-end'); + + /** + * Object that is being used to cache key handlers. + * @type {Object} + * @private + */ + this.keyHandlers_ = {}; + + /** + * Collection of dates that make up the date picker. + * @type {!Array>} + * @private + */ + this.grid_ = []; + + /** @private {Array>} */ + this.elTable_; + + /** + * TODO(tbreisacher): Remove external references to this field, + * and make it private. + * @type {Element} + */ + this.tableBody_; + + /** @private {Element} */ + this.tableFoot_; + + /** @private {Element} */ + this.elYear_; + + /** @private {Element} */ + this.elMonth_; + + /** @private {Element} */ + this.elToday_; + + /** @private {Element} */ + this.elNone_; + + /** @private {Element} */ + this.menu_; + /** @private {Element} */ + this.menuSelected_; + + /** @private {?Element} */ + this.selectedCell_; + + /** @private {function(Element)} */ + this.menuCallback_; + + /** + * Number of rows in the picker table. Used for detecting size changes. + * @private {number} + */ + this.lastNumberOfRowsInGrid_ = 0; +}; +goog.inherits(goog.ui.DatePicker, goog.ui.Component); +goog.tagUnsealableClass(goog.ui.DatePicker); + + +/** + * Flag indicating if the number of weeks shown should be fixed. + * @type {boolean} + * @private + */ +goog.ui.DatePicker.prototype.showFixedNumWeeks_ = true; + + +/** + * Flag indicating if days from other months should be shown. + * @type {boolean} + * @private + */ +goog.ui.DatePicker.prototype.showOtherMonths_ = true; + + +/** + * Range of dates which are selectable by the user. + * @type {!goog.date.DateRange} + * @private + */ +goog.ui.DatePicker.prototype.userSelectableDateRange_ = + goog.date.DateRange.allTime(); + + +/** + * Flag indicating if extra week(s) always should be added at the end. If not + * set the extra week is added at the beginning if the number of days shown + * from the previous month is less than the number from the next month. + * @type {boolean} + * @private + */ +goog.ui.DatePicker.prototype.extraWeekAtEnd_ = true; + + +/** + * Flag indicating if week numbers should be shown. + * @type {boolean} + * @private + */ +goog.ui.DatePicker.prototype.showWeekNum_ = true; + + +/** + * Flag indicating if weekday names should be shown. + * @type {boolean} + * @private + */ +goog.ui.DatePicker.prototype.showWeekdays_ = true; + + +/** + * Flag indicating if none is a valid selection. Also controls if the none + * button should be shown or not. + * @type {boolean} + * @private + */ +goog.ui.DatePicker.prototype.allowNone_ = true; + + +/** + * Flag indicating if the today button should be shown. + * @type {boolean} + * @private + */ +goog.ui.DatePicker.prototype.showToday_ = true; + + +/** + * Flag indicating if the picker should use a simple navigation menu that only + * contains controls for navigating to the next and previous month. The default + * navigation menu contains controls for navigating to the next/previous month, + * next/previous year, and menus for jumping to specific months and years. + * @type {boolean} + * @private + */ +goog.ui.DatePicker.prototype.simpleNavigation_ = false; + + +/** + * Custom decorator function. Takes a goog.date.Date object, returns a String + * representing a CSS class or null if no special styling applies + * @type {?Function} + * @private + */ +goog.ui.DatePicker.prototype.decoratorFunction_ = null; + + +/** + * Flag indicating if the dates should be printed as a two charater date. + * @type {boolean} + * @private + */ +goog.ui.DatePicker.prototype.longDateFormat_ = false; + + +/** + * Element for navigation row on a datepicker. + * @type {?Element} + * @private + */ +goog.ui.DatePicker.prototype.elNavRow_ = null; + + +/** + * Element for the month/year in the navigation row. + * @type {?Element} + * @private + */ +goog.ui.DatePicker.prototype.elMonthYear_ = null; + + +/** + * Element for footer row on a datepicker. + * @type {?Element} + * @private + */ +goog.ui.DatePicker.prototype.elFootRow_ = null; + + +/** + * Generator for unique table cell IDs. + * @type {goog.ui.IdGenerator} + * @private + */ +goog.ui.DatePicker.prototype.cellIdGenerator_ = + goog.ui.IdGenerator.getInstance(); + + +/** + * Name of base CSS class of datepicker. + * @type {string} + * @private + */ +goog.ui.DatePicker.BASE_CSS_CLASS_ = goog.getCssName('goog-date-picker'); + + +/** + * The numbers of years to show before and after the current one in the + * year pull-down menu. A total of YEAR_MENU_RANGE * 2 + 1 will be shown. + * Example: for range = 2 and year 2013 => [2011, 2012, 2013, 2014, 2015] + * @const {number} + * @private + */ +goog.ui.DatePicker.YEAR_MENU_RANGE_ = 5; + + +/** + * The maximum number of rendered weeks a month can have. + * @const {number} + * @private + */ +goog.ui.DatePicker.MAX_NUM_WEEKS_ = 6; + + +/** + * Constants for event names + * + * @enum {string} + */ +goog.ui.DatePicker.Events = { + CHANGE: 'change', + CHANGE_ACTIVE_MONTH: 'changeActiveMonth', + GRID_SIZE_INCREASE: 'gridSizeIncrease', + SELECT: 'select' +}; + + +/** + * @deprecated Use isInDocument. + */ +goog.ui.DatePicker.prototype.isCreated = + goog.ui.DatePicker.prototype.isInDocument; + + +/** + * @return {number} The first day of week, 0 = Monday, 6 = Sunday. + */ +goog.ui.DatePicker.prototype.getFirstWeekday = function() { + return this.activeMonth_.getFirstDayOfWeek(); +}; + + +/** + * Returns the class name associated with specified weekday. + * @param {number} wday The week day number to get the class name for. + * @return {string} The class name associated with specified weekday. + */ +goog.ui.DatePicker.prototype.getWeekdayClass = function(wday) { + return this.wdayStyles_[wday]; +}; + + +/** + * @return {boolean} Whether a fixed number of weeks should be showed. If not + * only weeks for the current month will be shown. + */ +goog.ui.DatePicker.prototype.getShowFixedNumWeeks = function() { + return this.showFixedNumWeeks_; +}; + + +/** + * @return {boolean} Whether a days from the previous and/or next month should + * be shown. + */ +goog.ui.DatePicker.prototype.getShowOtherMonths = function() { + return this.showOtherMonths_; +}; + + +/** + * @return {boolean} Whether a the extra week(s) added always should be at the + * end. Only applicable if a fixed number of weeks are shown. + */ +goog.ui.DatePicker.prototype.getExtraWeekAtEnd = function() { + return this.extraWeekAtEnd_; +}; + + +/** + * @return {boolean} Whether week numbers should be shown. + */ +goog.ui.DatePicker.prototype.getShowWeekNum = function() { + return this.showWeekNum_; +}; + + +/** + * @return {boolean} Whether weekday names should be shown. + */ +goog.ui.DatePicker.prototype.getShowWeekdayNames = function() { + return this.showWeekdays_; +}; + + +/** + * @return {boolean} Whether none is a valid selection. + */ +goog.ui.DatePicker.prototype.getAllowNone = function() { + return this.allowNone_; +}; + + +/** + * @return {boolean} Whether the today button should be shown. + */ +goog.ui.DatePicker.prototype.getShowToday = function() { + return this.showToday_; +}; + + +/** + * Returns base CSS class. This getter is used to get base CSS class part. + * All CSS class names in component are created as: + * goog.getCssName(this.getBaseCssClass(), 'CLASS_NAME') + * @return {string} Base CSS class. + */ +goog.ui.DatePicker.prototype.getBaseCssClass = function() { + return goog.ui.DatePicker.BASE_CSS_CLASS_; +}; + + +/** + * Sets the first day of week + * + * @param {number} wday Week day, 0 = Monday, 6 = Sunday. + */ +goog.ui.DatePicker.prototype.setFirstWeekday = function(wday) { + this.activeMonth_.setFirstDayOfWeek(wday); + this.updateCalendarGrid_(); + this.redrawWeekdays_(); +}; + + +/** + * Sets class name associated with specified weekday. + * + * @param {number} wday Week day, 0 = Monday, 6 = Sunday. + * @param {string} className Class name. + */ +goog.ui.DatePicker.prototype.setWeekdayClass = function(wday, className) { + this.wdayStyles_[wday] = className; + this.redrawCalendarGrid_(); +}; + + +/** + * Sets whether a fixed number of weeks should be showed. If not only weeks + * for the current month will be showed. + * + * @param {boolean} b Whether a fixed number of weeks should be showed. + */ +goog.ui.DatePicker.prototype.setShowFixedNumWeeks = function(b) { + this.showFixedNumWeeks_ = b; + this.updateCalendarGrid_(); +}; + + +/** + * Sets whether a days from the previous and/or next month should be shown. + * + * @param {boolean} b Whether a days from the previous and/or next month should + * be shown. + */ +goog.ui.DatePicker.prototype.setShowOtherMonths = function(b) { + this.showOtherMonths_ = b; + this.redrawCalendarGrid_(); +}; + + +/** + * Sets the range of dates which may be selected by the user. + * + * @param {!goog.date.DateRange} dateRange The range of selectable dates. + */ +goog.ui.DatePicker.prototype.setUserSelectableDateRange = function(dateRange) { + this.userSelectableDateRange_ = dateRange; +}; + + +/** + * Gets the range of dates which may be selected by the user. + * + * @return {!goog.date.DateRange} The range of selectable dates. + */ +goog.ui.DatePicker.prototype.getUserSelectableDateRange = function() { + return this.userSelectableDateRange_; +}; + + +/** + * Determine if a date may be selected by the user. + * + * @param {!goog.date.Date} date The date to be tested. + * @return {boolean} Whether the user may select this date. + * @private + */ +goog.ui.DatePicker.prototype.isUserSelectableDate_ = function(date) { + return this.userSelectableDateRange_.contains(date); +}; + + +/** + * Sets whether the picker should use a simple navigation menu that only + * contains controls for navigating to the next and previous month. The default + * navigation menu contains controls for navigating to the next/previous month, + * next/previous year, and menus for jumping to specific months and years. + * + * @param {boolean} b Whether to use a simple navigation menu. + */ +goog.ui.DatePicker.prototype.setUseSimpleNavigationMenu = function(b) { + this.simpleNavigation_ = b; + this.updateNavigationRow_(); + this.updateCalendarGrid_(); +}; + + +/** + * Sets whether a the extra week(s) added always should be at the end. Only + * applicable if a fixed number of weeks are shown. + * + * @param {boolean} b Whether a the extra week(s) added always should be at the + * end. + */ +goog.ui.DatePicker.prototype.setExtraWeekAtEnd = function(b) { + this.extraWeekAtEnd_ = b; + this.updateCalendarGrid_(); +}; + + +/** + * Sets whether week numbers should be shown. + * + * @param {boolean} b Whether week numbers should be shown. + */ +goog.ui.DatePicker.prototype.setShowWeekNum = function(b) { + this.showWeekNum_ = b; + // The navigation and footer rows may rely on the number of visible columns, + // so we update them when adding/removing the weeknum column. + this.updateNavigationRow_(); + this.updateFooterRow_(); + this.updateCalendarGrid_(); +}; + + +/** + * Sets whether weekday names should be shown. + * + * @param {boolean} b Whether weekday names should be shown. + */ +goog.ui.DatePicker.prototype.setShowWeekdayNames = function(b) { + this.showWeekdays_ = b; + this.redrawWeekdays_(); + this.redrawCalendarGrid_(); +}; + + +/** + * Sets whether the picker uses narrow weekday names ('M', 'T', 'W', ...). + * + * The default behavior is to use short names ('Mon', 'Tue', 'Wed', ...). + * + * @param {boolean} b Whether to use narrow weekday names. + */ +goog.ui.DatePicker.prototype.setUseNarrowWeekdayNames = function(b) { + this.wdayNames_ = b ? this.symbols_.STANDALONENARROWWEEKDAYS : + this.symbols_.STANDALONESHORTWEEKDAYS; + this.redrawWeekdays_(); +}; + + +/** + * Sets whether none is a valid selection. + * + * @param {boolean} b Whether none is a valid selection. + */ +goog.ui.DatePicker.prototype.setAllowNone = function(b) { + this.allowNone_ = b; + if (this.elNone_) { + this.updateTodayAndNone_(); + } +}; + + +/** + * Sets whether the today button should be shown. + * + * @param {boolean} b Whether the today button should be shown. + */ +goog.ui.DatePicker.prototype.setShowToday = function(b) { + this.showToday_ = b; + if (this.elToday_) { + this.updateTodayAndNone_(); + } +}; + + +/** + * Updates the display style of the None and Today buttons as well as hides the + * table foot if both are hidden. + * @private + */ +goog.ui.DatePicker.prototype.updateTodayAndNone_ = function() { + goog.style.setElementShown(this.elToday_, this.showToday_); + goog.style.setElementShown(this.elNone_, this.allowNone_); + goog.style.setElementShown( + this.tableFoot_, this.showToday_ || this.allowNone_); +}; + + +/** + * Sets the decorator function. The function should have the interface of + * {string} f({goog.date.Date}); + * and return a String representing a CSS class to decorate the cell + * corresponding to the date specified. + * + * @param {Function} f The decorator function. + */ +goog.ui.DatePicker.prototype.setDecorator = function(f) { + this.decoratorFunction_ = f; +}; + + +/** + * Sets whether the date will be printed in long format. In long format, dates + * such as '1' will be printed as '01'. + * + * @param {boolean} b Whethere dates should be printed in long format. + */ +goog.ui.DatePicker.prototype.setLongDateFormat = function(b) { + this.longDateFormat_ = b; + this.redrawCalendarGrid_(); +}; + + +/** + * Changes the active month to the previous one. + */ +goog.ui.DatePicker.prototype.previousMonth = function() { + this.activeMonth_.add(new goog.date.Interval(goog.date.Interval.MONTHS, -1)); + this.updateCalendarGrid_(); + this.fireChangeActiveMonthEvent_(); +}; + + +/** + * Changes the active month to the next one. + */ +goog.ui.DatePicker.prototype.nextMonth = function() { + this.activeMonth_.add(new goog.date.Interval(goog.date.Interval.MONTHS, 1)); + this.updateCalendarGrid_(); + this.fireChangeActiveMonthEvent_(); +}; + + +/** + * Changes the active year to the previous one. + */ +goog.ui.DatePicker.prototype.previousYear = function() { + this.activeMonth_.add(new goog.date.Interval(goog.date.Interval.YEARS, -1)); + this.updateCalendarGrid_(); + this.fireChangeActiveMonthEvent_(); +}; + + +/** + * Changes the active year to the next one. + */ +goog.ui.DatePicker.prototype.nextYear = function() { + this.activeMonth_.add(new goog.date.Interval(goog.date.Interval.YEARS, 1)); + this.updateCalendarGrid_(); + this.fireChangeActiveMonthEvent_(); +}; + + +/** + * Selects the current date. + */ +goog.ui.DatePicker.prototype.selectToday = function() { + this.setDate(new goog.date.Date()); +}; + + +/** + * Clears the selection. + */ +goog.ui.DatePicker.prototype.selectNone = function() { + if (this.allowNone_) { + this.setDate(null); + } +}; + + +/** + * @return {!goog.date.Date} The active month displayed. + */ +goog.ui.DatePicker.prototype.getActiveMonth = function() { + return this.activeMonth_.clone(); +}; + + +/** + * @return {goog.date.Date} The selected date or null if nothing is selected. + */ +goog.ui.DatePicker.prototype.getDate = function() { + return this.date_ && this.date_.clone(); +}; + + +/** + * @param {number} row The row in the grid. + * @param {number} col The column in the grid. + * @return {goog.date.Date} The date in the grid or null if there is none. + */ +goog.ui.DatePicker.prototype.getDateAt = function(row, col) { + return this.grid_[row] ? + this.grid_[row][col] ? this.grid_[row][col].clone() : null : + null; +}; + + +/** + * Returns a date element given a row and column. In elTable_, the elements that + * represent dates are 1 indexed because of other elements such as headers. + * This corrects for the offset and makes the API 0 indexed. + * + * @param {number} row The row in the element table. + * @param {number} col The column in the element table. + * @return {Element} The element in the grid or null if there is none. + * @protected + */ +goog.ui.DatePicker.prototype.getDateElementAt = function(row, col) { + if (row < 0 || col < 0) { + return null; + } + var adjustedRow = row + 1; + return this.elTable_[adjustedRow] ? + this.elTable_[adjustedRow][col + 1] || null : + null; +}; + + +/** + * Sets the selected date. Will always fire the SELECT event. + * + * @param {goog.date.Date|Date} date Date to select or null to select nothing. + */ +goog.ui.DatePicker.prototype.setDate = function(date) { + this.setDate_(date, true); +}; + + +/** + * Sets the selected date, and optionally fires the SELECT event based on param. + * + * @param {goog.date.Date|Date} date Date to select or null to select nothing. + * @param {boolean} fireSelection Whether to fire the selection event. + * @private + */ +goog.ui.DatePicker.prototype.setDate_ = function(date, fireSelection) { + // Check if the month has been changed. + var sameMonth = date == this.date_ || + date && this.date_ && date.getFullYear() == this.date_.getFullYear() && + date.getMonth() == this.date_.getMonth(); + + // Check if the date has been changed. + var sameDate = + date == this.date_ || sameMonth && date.getDate() == this.date_.getDate(); + + // Set current date to clone of supplied goog.date.Date or Date. + this.date_ = date && new goog.date.Date(date); + + // Set current month + if (date) { + this.activeMonth_.set(this.date_); + // Set years with two digits to their full year, not 19XX. + this.activeMonth_.setFullYear(this.date_.getFullYear()); + this.activeMonth_.setDate(1); + } + + // Update calendar grid even if the date has not changed as even if today is + // selected another month can be displayed. + this.updateCalendarGrid_(); + + if (fireSelection) { + // TODO(eae): Standardize selection and change events with other components. + // Fire select event. + var selectEvent = new goog.ui.DatePickerEvent( + goog.ui.DatePicker.Events.SELECT, this, this.date_); + this.dispatchEvent(selectEvent); + } + + // Fire change event. + if (!sameDate) { + var changeEvent = new goog.ui.DatePickerEvent( + goog.ui.DatePicker.Events.CHANGE, this, this.date_); + this.dispatchEvent(changeEvent); + } + + // Fire change active month event. + if (!sameMonth) { + this.fireChangeActiveMonthEvent_(); + } +}; + + +/** + * Updates the navigation row (navigating months and maybe years) in the navRow_ + * element of a created picker. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.DatePicker.prototype.updateNavigationRow_ = function() { + if (!this.elNavRow_) { + return; + } + var row = this.elNavRow_; + + // Clear the navigation row. + while (row.firstChild) { + row.removeChild(row.firstChild); + } + + var fullDateFormat = + this.symbols_.DATEFORMATS[goog.i18n.DateTimeFormat.Format.FULL_DATE] + .toLowerCase(); + this.renderer_.renderNavigationRow( + row, this.simpleNavigation_, this.showWeekNum_, fullDateFormat); + + if (this.simpleNavigation_) { + this.addPreventDefaultClickHandler_( + row, goog.getCssName(this.getBaseCssClass(), 'previousMonth'), + this.previousMonth); + var previousMonthElement = goog.dom.getElementByClass( + goog.getCssName(this.getBaseCssClass(), 'previousMonth'), row); + if (previousMonthElement) { + // Note: we're hiding the next and previous month buttons from screen + // readers because keyboard navigation doesn't currently work correctly + // with them. If that is fixed, we can show the buttons again. + goog.a11y.aria.setState( + previousMonthElement, goog.a11y.aria.State.HIDDEN, true); + previousMonthElement.tabIndex = -1; + } + + this.addPreventDefaultClickHandler_( + row, goog.getCssName(this.getBaseCssClass(), 'nextMonth'), + this.nextMonth); + var nextMonthElement = goog.dom.getElementByClass( + goog.getCssName(this.getBaseCssClass(), 'nextMonth'), row); + if (nextMonthElement) { + goog.a11y.aria.setState( + nextMonthElement, goog.a11y.aria.State.HIDDEN, true); + nextMonthElement.tabIndex = -1; + } + + this.elMonthYear_ = goog.dom.getElementByClass( + goog.getCssName(this.getBaseCssClass(), 'monthyear'), row); + } else { + this.addPreventDefaultClickHandler_( + row, goog.getCssName(this.getBaseCssClass(), 'previousMonth'), + this.previousMonth); + this.addPreventDefaultClickHandler_( + row, goog.getCssName(this.getBaseCssClass(), 'nextMonth'), + this.nextMonth); + this.addPreventDefaultClickHandler_( + row, goog.getCssName(this.getBaseCssClass(), 'month'), + this.showMonthMenu_); + + this.addPreventDefaultClickHandler_( + row, goog.getCssName(this.getBaseCssClass(), 'previousYear'), + this.previousYear); + this.addPreventDefaultClickHandler_( + row, goog.getCssName(this.getBaseCssClass(), 'nextYear'), + this.nextYear); + this.addPreventDefaultClickHandler_( + row, goog.getCssName(this.getBaseCssClass(), 'year'), + this.showYearMenu_); + + this.elMonth_ = goog.dom.getElementByClass( + goog.getCssName(this.getBaseCssClass(), 'month'), row); + this.elYear_ = goog.dom.getDomHelper().getElementByClass( + goog.getCssName(this.getBaseCssClass(), 'year'), row); + } +}; + + +/** + * Setup click handler with prevent default. + * + * @param {!Element} parentElement The parent element of the element. This is + * needed because the element in question might not be in the dom yet. + * @param {string} cssName The CSS class name of the element to attach a click + * handler. + * @param {Function} handlerFunction The click handler function. + * @private + */ +goog.ui.DatePicker.prototype.addPreventDefaultClickHandler_ = function( + parentElement, cssName, handlerFunction) { + var element = goog.dom.getElementByClass(cssName, parentElement); + this.getHandler().listen(element, goog.events.EventType.CLICK, function(e) { + e.preventDefault(); + handlerFunction.call(this, e); + }); +}; + + +/** + * Updates the footer row (with select buttons) in the footRow_ element of a + * created picker. + * @private + */ +goog.ui.DatePicker.prototype.updateFooterRow_ = function() { + if (!this.elFootRow_) { + return; + } + + var row = this.elFootRow_; + + // Clear the footer row. + goog.dom.removeChildren(row); + + this.renderer_.renderFooterRow(row, this.showWeekNum_); + + this.addPreventDefaultClickHandler_( + row, goog.getCssName(this.getBaseCssClass(), 'today-btn'), + this.selectToday); + this.addPreventDefaultClickHandler_( + row, goog.getCssName(this.getBaseCssClass(), 'none-btn'), + this.selectNone); + + this.elToday_ = goog.dom.getElementByClass( + goog.getCssName(this.getBaseCssClass(), 'today-btn'), row); + this.elNone_ = goog.dom.getElementByClass( + goog.getCssName(this.getBaseCssClass(), 'none-btn'), row); + + this.updateTodayAndNone_(); +}; + + +/** + * @override + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.DatePicker.prototype.decorateInternal = function(el) { + goog.ui.DatePicker.superClass_.decorateInternal.call(this, el); + goog.asserts.assert(el); + goog.dom.classlist.add(el, this.getBaseCssClass()); + + var table = + this.dom_.createDom(goog.dom.TagName.TABLE, {'role': 'presentation'}); + var thead = this.dom_.createDom(goog.dom.TagName.THEAD); + var tbody = this.dom_.createDom(goog.dom.TagName.TBODY, {'role': 'grid'}); + var tfoot = this.dom_.createDom(goog.dom.TagName.TFOOT); + + tbody.tabIndex = 0; + + // As per comment in colorpicker: table.tBodies and table.tFoot should not be + // used because of a bug in Safari, hence using an instance variable + this.tableBody_ = tbody; + this.tableFoot_ = tfoot; + + var row = this.dom_.createDom(goog.dom.TagName.TR, {'role': 'row'}); + row.className = goog.getCssName(this.getBaseCssClass(), 'head'); + this.elNavRow_ = row; + this.updateNavigationRow_(); + + thead.appendChild(row); + + var cell; + this.elTable_ = []; + for (var i = 0; i < 7; i++) { + row = this.dom_.createElement(goog.dom.TagName.TR); + this.elTable_[i] = []; + for (var j = 0; j < 8; j++) { + cell = this.dom_.createElement(j == 0 || i == 0 ? 'th' : 'td'); + if ((j == 0 || i == 0) && j != i) { + cell.className = (j == 0) ? + goog.getCssName(this.getBaseCssClass(), 'week') : + goog.getCssName(this.getBaseCssClass(), 'wday'); + goog.a11y.aria.setRole(cell, j == 0 ? 'rowheader' : 'columnheader'); + } else if (i !== 0 && j !== 0) { + goog.a11y.aria.setRole(cell, 'gridcell'); + // Make the cells programmatically-focusable (see focus() call below). + cell.setAttribute('tabindex', '-1'); + } + row.appendChild(cell); + this.elTable_[i][j] = cell; + } + tbody.appendChild(row); + } + + row = this.dom_.createElement(goog.dom.TagName.TR); + row.className = goog.getCssName(this.getBaseCssClass(), 'foot'); + this.elFootRow_ = row; + this.updateFooterRow_(); + tfoot.appendChild(row); + + + table.cellSpacing = '0'; + table.cellPadding = '0'; + table.appendChild(thead); + table.appendChild(tbody); + table.appendChild(tfoot); + el.appendChild(table); + + this.redrawWeekdays_(); + this.updateCalendarGrid_(); + + el.tabIndex = 0; +}; + + +/** @override */ +goog.ui.DatePicker.prototype.createDom = function() { + goog.ui.DatePicker.superClass_.createDom.call(this); + this.decorateInternal(this.getElement()); +}; + + +/** @override */ +goog.ui.DatePicker.prototype.enterDocument = function() { + goog.ui.DatePicker.superClass_.enterDocument.call(this); + + var eh = this.getHandler(); + eh.listen( + this.tableBody_, goog.events.EventType.CLICK, this.handleGridClick_); + eh.listen( + this.getKeyHandlerForElement_(this.getElement()), + goog.events.KeyHandler.EventType.KEY, this.handleGridKeyPress_); +}; + + +/** @override */ +goog.ui.DatePicker.prototype.exitDocument = function() { + goog.ui.DatePicker.superClass_.exitDocument.call(this); + this.destroyMenu_(); + for (var uid in this.keyHandlers_) { + this.keyHandlers_[uid].dispose(); + } + this.keyHandlers_ = {}; +}; + + +/** + * @deprecated Use decorate instead. + */ +goog.ui.DatePicker.prototype.create = goog.ui.DatePicker.prototype.decorate; + + +/** @override */ +goog.ui.DatePicker.prototype.disposeInternal = function() { + goog.ui.DatePicker.superClass_.disposeInternal.call(this); + + this.elTable_ = null; + this.tableBody_ = null; + this.tableFoot_ = null; + this.elNavRow_ = null; + this.elFootRow_ = null; + this.elMonth_ = null; + this.elMonthYear_ = null; + this.elYear_ = null; + this.elToday_ = null; + this.elNone_ = null; +}; + + +/** + * Click handler for date grid. + * @param {goog.events.BrowserEvent} event Click event. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.DatePicker.prototype.handleGridClick_ = function(event) { + if (event.target.tagName == goog.dom.TagName.TD) { + // colIndex/rowIndex is broken in Safari, find position by looping + var el, x = -2, y = -2; // first col/row is for weekday/weeknum + for (el = event.target; el; el = el.previousSibling, x++) { + } + for (el = event.target.parentNode; el; el = el.previousSibling, y++) { + } + var obj = this.grid_[y][x]; + if (this.isUserSelectableDate_(obj)) { + this.setDate(obj.clone()); + } + } +}; + + +/** + * Keypress handler for date grid. + * + * @param {goog.events.BrowserEvent} event Keypress event. + * @private + */ +goog.ui.DatePicker.prototype.handleGridKeyPress_ = function(event) { + var months, days; + switch (event.keyCode) { + case 33: // Page up + event.preventDefault(); + months = -1; + break; + case 34: // Page down + event.preventDefault(); + months = 1; + break; + case 37: // Left + event.preventDefault(); + days = -1; + break; + case 39: // Right + event.preventDefault(); + days = 1; + break; + case 38: // Down + event.preventDefault(); + days = -7; + break; + case 40: // Up + event.preventDefault(); + days = 7; + break; + case 36: // Home + event.preventDefault(); + this.selectToday(); + break; + case 46: // Delete + event.preventDefault(); + this.selectNone(); + break; + case 13: // Enter + case 32: // Space + event.preventDefault(); + this.setDate_(this.date_, true /* fireSelection */); + default: + return; + } + var date; + if (this.date_) { + date = this.date_.clone(); + date.add(new goog.date.Interval(0, months, days)); + } else { + date = this.activeMonth_.clone(); + date.setDate(1); + } + if (this.isUserSelectableDate_(date)) { + this.setDate_(date, false /* fireSelection */); + // Focus the currently-selected cell to surface its aria-label for assistive + // tech, (eg) allowing unsighted users to see what the arrow keys are doing. + this.selectedCell_.focus(); + } +}; + + +/** + * Click handler for month button. Opens month selection menu. + * + * @param {goog.events.BrowserEvent} event Click event. + * @private + */ +goog.ui.DatePicker.prototype.showMonthMenu_ = function(event) { + event.stopPropagation(); + + var list = []; + for (var i = 0; i < 12; i++) { + list.push(this.symbols_.STANDALONEMONTHS[i]); + } + this.createMenu_( + this.elMonth_, list, this.handleMonthMenuClick_, + this.symbols_.STANDALONEMONTHS[this.activeMonth_.getMonth()]); +}; + + +/** + * Click handler for year button. Opens year selection menu. + * + * @param {goog.events.BrowserEvent} event Click event. + * @private + */ +goog.ui.DatePicker.prototype.showYearMenu_ = function(event) { + event.stopPropagation(); + + var list = []; + var year = this.activeMonth_.getFullYear(); + var loopDate = this.activeMonth_.clone(); + for (var i = -goog.ui.DatePicker.YEAR_MENU_RANGE_; + i <= goog.ui.DatePicker.YEAR_MENU_RANGE_; i++) { + loopDate.setFullYear(year + i); + list.push(this.i18nDateFormatterYear_.format(loopDate)); + } + this.createMenu_( + this.elYear_, list, this.handleYearMenuClick_, + this.i18nDateFormatterYear_.format(this.activeMonth_)); +}; + + +/** + * Call back function for month menu. + * + * @param {Element} target Selected item. + * @private + */ +goog.ui.DatePicker.prototype.handleMonthMenuClick_ = function(target) { + var itemIndex = Number(target.getAttribute('itemIndex')); + this.activeMonth_.setMonth(itemIndex); + this.updateCalendarGrid_(); + + if (this.elMonth_.focus) { + this.elMonth_.focus(); + } +}; + + +/** + * Call back function for year menu. + * + * @param {Element} target Selected item. + * @private + */ +goog.ui.DatePicker.prototype.handleYearMenuClick_ = function(target) { + if (target.firstChild.nodeType == goog.dom.NodeType.TEXT) { + // We use the same technique used for months to get the position of the + // item in the menu, as the year is not necessarily numeric. + var itemIndex = Number(target.getAttribute('itemIndex')); + var year = this.activeMonth_.getFullYear(); + this.activeMonth_.setFullYear( + year + itemIndex - goog.ui.DatePicker.YEAR_MENU_RANGE_); + this.updateCalendarGrid_(); + } + + this.elYear_.focus(); +}; + + +/** + * Support function for menu creation. + * @param {Element} srcEl Button to create menu for. + * @param {Array} items List of items to populate menu with. + * @param {function(Element)} method Call back method. + * @param {string} selected Item to mark as selected in menu. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.DatePicker.prototype.createMenu_ = function( + srcEl, items, method, selected) { + this.destroyMenu_(); + + var el = this.dom_.createElement(goog.dom.TagName.DIV); + el.className = goog.getCssName(this.getBaseCssClass(), 'menu'); + + this.menuSelected_ = null; + + var ul = this.dom_.createElement(goog.dom.TagName.UL); + for (var i = 0; i < items.length; i++) { + var li = this.dom_.createDom(goog.dom.TagName.LI, null, items[i]); + li.setAttribute('itemIndex', i); + if (items[i] == selected) { + this.menuSelected_ = li; + } + ul.appendChild(li); + } + el.appendChild(ul); + srcEl = /** @type {!HTMLElement} */ (srcEl); + el.style.left = srcEl.offsetLeft + srcEl.parentNode.offsetLeft + 'px'; + el.style.top = srcEl.offsetTop + 'px'; + el.style.width = srcEl.clientWidth + 'px'; + this.elMonth_.parentNode.appendChild(el); + + this.menu_ = el; + if (!this.menuSelected_) { + this.menuSelected_ = /** @type {Element} */ (ul.firstChild); + } + this.menuSelected_.className = + goog.getCssName(this.getBaseCssClass(), 'menu-selected'); + this.menuCallback_ = method; + + var eh = this.getHandler(); + eh.listen(this.menu_, goog.events.EventType.CLICK, this.handleMenuClick_); + eh.listen( + this.getKeyHandlerForElement_(this.menu_), + goog.events.KeyHandler.EventType.KEY, this.handleMenuKeyPress_); + eh.listen( + this.dom_.getDocument(), goog.events.EventType.CLICK, this.destroyMenu_); + el.tabIndex = 0; + el.focus(); +}; + + +/** + * Click handler for menu. + * + * @param {goog.events.BrowserEvent} event Click event. + * @private + */ +goog.ui.DatePicker.prototype.handleMenuClick_ = function(event) { + event.stopPropagation(); + + this.destroyMenu_(); + if (this.menuCallback_) { + this.menuCallback_(/** @type {Element} */ (event.target)); + } +}; + + +/** + * Keypress handler for menu. + * @param {goog.events.BrowserEvent} event Keypress event. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.DatePicker.prototype.handleMenuKeyPress_ = function(event) { + // Prevent the grid keypress handler from catching the keypress event. + event.stopPropagation(); + + var el; + var menuSelected = this.menuSelected_; + switch (event.keyCode) { + case 35: // End + event.preventDefault(); + el = menuSelected.parentNode.lastChild; + break; + case 36: // Home + event.preventDefault(); + el = menuSelected.parentNode.firstChild; + break; + case 38: // Up + event.preventDefault(); + el = menuSelected.previousSibling; + break; + case 40: // Down + event.preventDefault(); + el = menuSelected.nextSibling; + break; + case 13: // Enter + case 9: // Tab + case 0: // Space + event.preventDefault(); + this.destroyMenu_(); + this.menuCallback_(menuSelected); + break; + } + if (el && el != menuSelected) { + menuSelected.className = ''; + el.className = goog.getCssName(this.getBaseCssClass(), 'menu-selected'); + this.menuSelected_ = /** @type {!Element} */ (el); + } +}; + + +/** + * Support function for menu destruction. + * @private + */ +goog.ui.DatePicker.prototype.destroyMenu_ = function() { + if (this.menu_) { + var eh = this.getHandler(); + eh.unlisten(this.menu_, goog.events.EventType.CLICK, this.handleMenuClick_); + eh.unlisten( + this.getKeyHandlerForElement_(this.menu_), + goog.events.KeyHandler.EventType.KEY, this.handleMenuKeyPress_); + eh.unlisten( + this.dom_.getDocument(), goog.events.EventType.CLICK, + this.destroyMenu_); + goog.dom.removeNode(this.menu_); + delete this.menu_; + } +}; + + +/** + * Determines the dates/weekdays for the current month and builds an in memory + * representation of the calendar. + * + * @private + */ +goog.ui.DatePicker.prototype.updateCalendarGrid_ = function() { + if (!this.getElement()) { + return; + } + + var date = this.activeMonth_.clone(); + date.setDate(1); + + // Show year name of select month + if (this.elMonthYear_) { + goog.dom.setTextContent( + this.elMonthYear_, this.i18nDateFormatterMonthYear_.format(date)); + } + if (this.elMonth_) { + goog.dom.setTextContent( + this.elMonth_, this.symbols_.STANDALONEMONTHS[date.getMonth()]); + } + if (this.elYear_) { + goog.dom.setTextContent( + this.elYear_, this.i18nDateFormatterYear_.format(date)); + } + + var wday = date.getWeekday(); + var days = date.getNumberOfDaysInMonth(); + + // Determine how many days to show for previous month + date.add(new goog.date.Interval(goog.date.Interval.MONTHS, -1)); + date.setDate(date.getNumberOfDaysInMonth() - (wday - 1)); + + if (this.showFixedNumWeeks_ && !this.extraWeekAtEnd_ && days + wday < 33) { + date.add(new goog.date.Interval(goog.date.Interval.DAYS, -7)); + } + + // Create weekday/day grid + var dayInterval = new goog.date.Interval(goog.date.Interval.DAYS, 1); + this.grid_ = []; + for (var y = 0; y < 6; y++) { // Weeks + this.grid_[y] = []; + for (var x = 0; x < 7; x++) { // Weekdays + this.grid_[y][x] = date.clone(); + // Date.add breaks dates before year 100 by adding 1900 to the year + // value. As a workaround we store the year before the add and reapply it + // after (with special handling for January 1st). + var year = date.getFullYear(); + date.add(dayInterval); + if (date.getMonth() == 0 && date.getDate() == 1) { + // Increase year on January 1st. + year++; + } + date.setFullYear(year); + } + } + + this.redrawCalendarGrid_(); +}; + + +/** + * Draws calendar view from in memory representation and applies class names + * depending on the selection, weekday and whatever the day belongs to the + * active month or not. + * @private + */ +goog.ui.DatePicker.prototype.redrawCalendarGrid_ = function() { + if (!this.getElement()) { + return; + } + + var month = this.activeMonth_.getMonth(); + var today = new goog.date.Date(); + var todayYear = today.getFullYear(); + var todayMonth = today.getMonth(); + var todayDate = today.getDate(); + // The maximum number of weeks a month can have is 6. This is decreased below + // if weeks are hidden. + var numberOfWeeksInThisMonth = goog.ui.DatePicker.MAX_NUM_WEEKS_; + + // Draw calendar week by week, a worst case month has six weeks. + for (var y = 0; y < goog.ui.DatePicker.MAX_NUM_WEEKS_; y++) { + // Draw week number, if enabled + if (this.showWeekNum_) { + goog.dom.setTextContent( + this.elTable_[y + 1][0], + this.i18nDateFormatterWeek_.format(this.grid_[y][0])); + goog.dom.classlist.set( + this.elTable_[y + 1][0], + goog.getCssName(this.getBaseCssClass(), 'week')); + } else { + goog.dom.setTextContent(this.elTable_[y + 1][0], ''); + goog.dom.classlist.set(this.elTable_[y + 1][0], ''); + } + + for (var x = 0; x < 7; x++) { + var o = this.grid_[y][x]; + var el = this.elTable_[y + 1][x + 1]; + + // Assign a unique element id (required for setting the active descendant + // ARIA role) unless already set. + if (!el.id) { + el.id = this.cellIdGenerator_.getNextUniqueId(); + } + goog.asserts.assert(el, 'The table DOM element cannot be null.'); + goog.a11y.aria.setRole(el, 'gridcell'); + // Set the aria label of the grid cell to the month plus the day. + goog.a11y.aria.setLabel( + el, this.i18nDateFormatterDayAriaLabel_.format(o)); + + var classes = [goog.getCssName(this.getBaseCssClass(), 'date')]; + if (!this.isUserSelectableDate_(o)) { + classes.push( + goog.getCssName(this.getBaseCssClass(), 'unavailable-date')); + } + if (this.showOtherMonths_ || o.getMonth() == month) { + // Date belongs to previous or next month + if (o.getMonth() != month) { + classes.push(goog.getCssName(this.getBaseCssClass(), 'other-month')); + } + + // Apply styles set by setWeekdayClass + var wday = (x + this.activeMonth_.getFirstDayOfWeek() + 7) % 7; + if (this.wdayStyles_[wday]) { + classes.push(this.wdayStyles_[wday]); + } + + // Current date + if (o.getDate() == todayDate && o.getMonth() == todayMonth && + o.getFullYear() == todayYear) { + classes.push(goog.getCssName(this.getBaseCssClass(), 'today')); + } + + // Selected date + if (this.date_ && o.getDate() == this.date_.getDate() && + o.getMonth() == this.date_.getMonth() && + o.getFullYear() == this.date_.getFullYear()) { + classes.push(goog.getCssName(this.getBaseCssClass(), 'selected')); + goog.asserts.assert( + this.tableBody_, 'The table body DOM element cannot be null'); + this.selectedCell_ = el; + } + + // Custom decorator + if (this.decoratorFunction_) { + var customClass = this.decoratorFunction_(o); + if (customClass) { + classes.push(customClass); + } + } + + // Set cell text to the date and apply classes. + var formattedDate = this.longDateFormat_ ? + this.i18nDateFormatterDay2_.format(o) : + this.i18nDateFormatterDay_.format(o); + goog.dom.setTextContent(el, formattedDate); + // Date belongs to previous or next month and showOtherMonths is false, + // clear text and classes. + } else { + goog.dom.setTextContent(el, ''); + } + goog.dom.classlist.set(el, classes.join(' ')); + } + + // Hide either the last one or last two weeks if they contain no days from + // the active month and the showFixedNumWeeks is false. The first four weeks + // are always shown as no month has less than 28 days). + if (y >= 4) { + var parentEl = /** @type {Element} */ ( + this.elTable_[y + 1][0].parentElement || + this.elTable_[y + 1][0].parentNode); + var doesMonthHaveThisWeek = this.grid_[y][0].getMonth() == month; + goog.style.setElementShown( + parentEl, doesMonthHaveThisWeek || this.showFixedNumWeeks_); + + if (!doesMonthHaveThisWeek) { + numberOfWeeksInThisMonth = Math.min(numberOfWeeksInThisMonth, y); + } + } + } + + var numberOfRowsInGrid = + (this.showFixedNumWeeks_ ? goog.ui.DatePicker.MAX_NUM_WEEKS_ : + numberOfWeeksInThisMonth) + + (this.showWeekdays_ ? 1 : 0); + + if (this.lastNumberOfRowsInGrid_ != numberOfRowsInGrid) { + if (this.lastNumberOfRowsInGrid_ < numberOfRowsInGrid) { + this.dispatchEvent(goog.ui.DatePicker.Events.GRID_SIZE_INCREASE); + } + this.lastNumberOfRowsInGrid_ = numberOfRowsInGrid; + } +}; + + +/** + * Fires the CHANGE_ACTIVE_MONTH event. + * @private + */ +goog.ui.DatePicker.prototype.fireChangeActiveMonthEvent_ = function() { + var changeMonthEvent = new goog.ui.DatePickerEvent( + goog.ui.DatePicker.Events.CHANGE_ACTIVE_MONTH, this, + this.getActiveMonth()); + this.dispatchEvent(changeMonthEvent); +}; + + +/** + * Draw weekday names, if enabled. Start with whatever day has been set as the + * first day of week. + * @private + */ +goog.ui.DatePicker.prototype.redrawWeekdays_ = function() { + if (!this.getElement()) { + return; + } + if (this.showWeekdays_) { + for (var x = 0; x < 7; x++) { + var el = this.elTable_[0][x + 1]; + var wday = (x + this.activeMonth_.getFirstDayOfWeek() + 7) % 7; + goog.dom.setTextContent(el, this.wdayNames_[(wday + 1) % 7]); + } + } + var parentEl = /** @type {Element} */ ( + this.elTable_[0][0].parentElement || this.elTable_[0][0].parentNode); + goog.style.setElementShown(parentEl, this.showWeekdays_); +}; + + +/** + * Returns the key handler for an element and caches it so that it can be + * retrieved at a later point. + * @param {Element} el The element to get the key handler for. + * @return {goog.events.KeyHandler} The key handler for the element. + * @private + */ +goog.ui.DatePicker.prototype.getKeyHandlerForElement_ = function(el) { + var uid = goog.getUid(el); + if (!(uid in this.keyHandlers_)) { + this.keyHandlers_[uid] = new goog.events.KeyHandler(el); + } + return this.keyHandlers_[uid]; +}; + + + +/** + * Object representing a date picker event. + * + * @param {string} type Event type. + * @param {goog.ui.DatePicker} target Date picker initiating event. + * @param {goog.date.Date} date Selected date. + * @constructor + * @extends {goog.events.Event} + * @final + */ +goog.ui.DatePickerEvent = function(type, target, date) { + goog.events.Event.call(this, type, target); + + /** + * The selected date + * @type {goog.date.Date} + */ + this.date = date; +}; +goog.inherits(goog.ui.DatePickerEvent, goog.events.Event); diff --git a/closure-library/closure/goog/ui/datepickerrenderer.js b/closure-library/closure/goog/ui/datepickerrenderer.js new file mode 100644 index 0000000000..7bc5224638 --- /dev/null +++ b/closure-library/closure/goog/ui/datepickerrenderer.js @@ -0,0 +1,55 @@ +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview The renderer interface for {@link goog.ui.DatePicker}. + * + * @see ../demos/datepicker.html + */ + +goog.provide('goog.ui.DatePickerRenderer'); + + + +/** + * The renderer for {@link goog.ui.DatePicker}. Renders the date picker's + * navigation header and footer. + * @interface + */ +goog.ui.DatePickerRenderer = function() {}; + + +/** + * Render the navigation row. + * + * @param {!Element} row The parent element to render the component into. + * @param {boolean} simpleNavigation Whether the picker should render a simple + * navigation menu that only contains controls for navigating to the next + * and previous month. The default navigation menu contains controls for + * navigating to the next/previous month, next/previous year, and menus for + * jumping to specific months and years. + * @param {boolean} showWeekNum Whether week numbers should be shown. + * @param {string} fullDateFormat The full date format. + * {@see goog.i18n.DateTimeSymbols}. + */ +goog.ui.DatePickerRenderer.prototype.renderNavigationRow = goog.abstractMethod; + + +/** + * Render the footer row. + * + * @param {!Element} row The parent element to render the component into. + * @param {boolean} showWeekNum Whether week numbers should be shown. + */ +goog.ui.DatePickerRenderer.prototype.renderFooterRow = goog.abstractMethod; diff --git a/closure-library/closure/goog/ui/decorate.js b/closure-library/closure/goog/ui/decorate.js new file mode 100644 index 0000000000..8a493b887b --- /dev/null +++ b/closure-library/closure/goog/ui/decorate.js @@ -0,0 +1,40 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Provides a function that decorates an element based on its CSS + * class name. + * @author attila@google.com (Attila Bodis) + */ + +goog.provide('goog.ui.decorate'); + +goog.require('goog.ui.registry'); + +goog.forwardDeclare('goog.ui.Component'); + + +/** + * Decorates the element with a suitable {@link goog.ui.Component} instance, if + * a matching decorator is found. + * @param {Element} element Element to decorate. + * @return {goog.ui.Component?} New component instance, decorating the element. + */ +goog.ui.decorate = function(element) { + var decorator = goog.ui.registry.getDecorator(element); + if (decorator) { + decorator.decorate(element); + } + return decorator; +}; diff --git a/closure-library/closure/goog/ui/defaultdatepickerrenderer.js b/closure-library/closure/goog/ui/defaultdatepickerrenderer.js new file mode 100644 index 0000000000..56217faccb --- /dev/null +++ b/closure-library/closure/goog/ui/defaultdatepickerrenderer.js @@ -0,0 +1,210 @@ +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview The default renderer for {@link goog.ui.DatePicker}. + * + * @see ../demos/datepicker.html + */ + +goog.provide('goog.ui.DefaultDatePickerRenderer'); + +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +/** @suppress {extraRequire} Interface. */ +goog.require('goog.ui.DatePickerRenderer'); + + + +/** + * Default renderer for {@link goog.ui.DatePicker}. Renders the date picker's + * navigation header and footer. + * + * @param {string} baseCssClass Name of base CSS class of the date picker. + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper. + * @constructor + * @implements {goog.ui.DatePickerRenderer} + */ +goog.ui.DefaultDatePickerRenderer = function(baseCssClass, opt_domHelper) { + /** + * Name of base CSS class of datepicker + * @type {string} + * @private + */ + this.baseCssClass_ = baseCssClass; + + /** + * @type {!goog.dom.DomHelper} + * @private + */ + this.dom_ = opt_domHelper || goog.dom.getDomHelper(); +}; + + +/** + * Returns the dom helper that is being used on this component. + * @return {!goog.dom.DomHelper} The dom helper used on this component. + */ +goog.ui.DefaultDatePickerRenderer.prototype.getDomHelper = function() { + return this.dom_; +}; + + +/** + * Returns base CSS class. This getter is used to get base CSS class part. + * All CSS class names in component are created as: + * goog.getCssName(this.getBaseCssClass(), 'CLASS_NAME') + * @return {string} Base CSS class. + */ +goog.ui.DefaultDatePickerRenderer.prototype.getBaseCssClass = function() { + return this.baseCssClass_; +}; + + +/** + * Render the navigation row (navigating months and maybe years). + * + * @param {!Element} row The parent element to render the component into. + * @param {boolean} simpleNavigation Whether the picker should render a simple + * navigation menu that only contains controls for navigating to the next + * and previous month. The default navigation menu contains controls for + * navigating to the next/previous month, next/previous year, and menus for + * jumping to specific months and years. + * @param {boolean} showWeekNum Whether week numbers should be shown. + * @param {string} fullDateFormat The full date format. + * {@see goog.i18n.DateTimeSymbols}. + * @override + */ +goog.ui.DefaultDatePickerRenderer.prototype.renderNavigationRow = function( + row, simpleNavigation, showWeekNum, fullDateFormat) { + // Populate the navigation row according to the configured navigation mode. + var cell, monthCell, yearCell; + + if (simpleNavigation) { + cell = this.getDomHelper().createElement(goog.dom.TagName.TD); + cell.colSpan = showWeekNum ? 1 : 2; + this.createButton_( + cell, '\u00AB', + goog.getCssName(this.getBaseCssClass(), 'previousMonth')); // << + row.appendChild(cell); + + cell = this.getDomHelper().createElement(goog.dom.TagName.TD); + cell.colSpan = showWeekNum ? 6 : 5; + cell.className = goog.getCssName(this.getBaseCssClass(), 'monthyear'); + row.appendChild(cell); + + cell = this.getDomHelper().createElement(goog.dom.TagName.TD); + this.createButton_( + cell, '\u00BB', + goog.getCssName(this.getBaseCssClass(), 'nextMonth')); // >> + row.appendChild(cell); + + } else { + monthCell = this.getDomHelper().createElement(goog.dom.TagName.TD); + monthCell.colSpan = 5; + this.createButton_( + monthCell, '\u00AB', + goog.getCssName(this.getBaseCssClass(), 'previousMonth')); // << + this.createButton_( + monthCell, '', goog.getCssName(this.getBaseCssClass(), 'month')); + this.createButton_( + monthCell, '\u00BB', + goog.getCssName(this.getBaseCssClass(), 'nextMonth')); // >> + + yearCell = this.getDomHelper().createElement(goog.dom.TagName.TD); + yearCell.colSpan = 3; + this.createButton_( + yearCell, '\u00AB', + goog.getCssName(this.getBaseCssClass(), 'previousYear')); // << + this.createButton_( + yearCell, '', goog.getCssName(this.getBaseCssClass(), 'year')); + this.createButton_( + yearCell, '\u00BB', + goog.getCssName(this.getBaseCssClass(), 'nextYear')); // << + + // If the date format has year ('y') appearing first before month ('m'), + // show the year on the left hand side of the datepicker popup. Otherwise, + // show the month on the left side. This check assumes the data to be + // valid, and that all date formats contain month and year. + if (fullDateFormat.indexOf('y') < fullDateFormat.indexOf('m')) { + row.appendChild(yearCell); + row.appendChild(monthCell); + } else { + row.appendChild(monthCell); + row.appendChild(yearCell); + } + } +}; + + +/** + * Render the footer row (with select buttons). + * + * @param {!Element} row The parent element to render the component into. + * @param {boolean} showWeekNum Whether week numbers should be shown. + * @override + */ +goog.ui.DefaultDatePickerRenderer.prototype.renderFooterRow = function( + row, showWeekNum) { + // Populate the footer row with buttons for Today and None. + var cell = this.getDomHelper().createElement(goog.dom.TagName.TD); + cell.colSpan = showWeekNum ? 2 : 3; + cell.className = goog.getCssName(this.getBaseCssClass(), 'today-cont'); + + /** @desc Label for button that selects the current date. */ + var MSG_DATEPICKER_TODAY_BUTTON_LABEL = goog.getMsg('Today'); + this.createButton_( + cell, MSG_DATEPICKER_TODAY_BUTTON_LABEL, + goog.getCssName(this.getBaseCssClass(), 'today-btn')); + row.appendChild(cell); + + cell = this.getDomHelper().createElement(goog.dom.TagName.TD); + cell.colSpan = showWeekNum ? 4 : 3; + row.appendChild(cell); + + cell = this.getDomHelper().createElement(goog.dom.TagName.TD); + cell.colSpan = 2; + cell.className = goog.getCssName(this.getBaseCssClass(), 'none-cont'); + + /** @desc Label for button that clears the selection. */ + var MSG_DATEPICKER_NONE = goog.getMsg('None'); + this.createButton_( + cell, MSG_DATEPICKER_NONE, + goog.getCssName(this.getBaseCssClass(), 'none-btn')); + row.appendChild(cell); +}; + + +/** + * Support function for button creation. + * + * @param {Element} parentNode Container the button should be added to. + * @param {string} label Button label. + * @param {string=} opt_className Class name for button, which will be used + * in addition to "goog-date-picker-btn". + * @private + * @return {!Element} The created button element. + */ +goog.ui.DefaultDatePickerRenderer.prototype.createButton_ = function( + parentNode, label, opt_className) { + var classes = [goog.getCssName(this.getBaseCssClass(), 'btn')]; + if (opt_className) { + classes.push(opt_className); + } + var el = this.getDomHelper().createElement(goog.dom.TagName.BUTTON); + el.className = classes.join(' '); + el.appendChild(this.getDomHelper().createTextNode(label)); + parentNode.appendChild(el); + return el; +}; diff --git a/closure-library/closure/goog/ui/dialog.js b/closure-library/closure/goog/ui/dialog.js new file mode 100644 index 0000000000..8d51305d46 --- /dev/null +++ b/closure-library/closure/goog/ui/dialog.js @@ -0,0 +1,1631 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Class for showing simple modal dialog boxes. + * + * TODO(user): + * * Standardize CSS class names with other components + * * Add functionality to "host" other components in content area + * * Abstract out ButtonSet and make it more general + * @see ../demos/dialog.html + */ + +goog.provide('goog.ui.Dialog'); +goog.provide('goog.ui.Dialog.ButtonSet'); +goog.provide('goog.ui.Dialog.ButtonSet.DefaultButtons'); +goog.provide('goog.ui.Dialog.DefaultButtonCaptions'); +goog.provide('goog.ui.Dialog.DefaultButtonKeys'); +goog.provide('goog.ui.Dialog.Event'); +goog.provide('goog.ui.Dialog.EventType'); + +goog.require('goog.a11y.aria'); +goog.require('goog.a11y.aria.Role'); +goog.require('goog.a11y.aria.State'); +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.classlist'); +goog.require('goog.dom.safe'); +goog.require('goog.events'); +goog.require('goog.events.Event'); +goog.require('goog.events.EventType'); +goog.require('goog.events.KeyCodes'); +goog.require('goog.events.Keys'); +goog.require('goog.fx.Dragger'); +goog.require('goog.html.SafeHtml'); +goog.require('goog.math.Rect'); +goog.require('goog.string'); +goog.require('goog.structs.Map'); +goog.require('goog.style'); +goog.require('goog.ui.ModalPopup'); + + + +/** + * Class for showing simple dialog boxes. + * The Html structure of the dialog box is: + *
      + *  Element         Function                Class-name, modal-dialog = default
      + * ----------------------------------------------------------------------------
      + * - iframe         Iframe mask              modal-dialog-bg
      + * - div            Background mask          modal-dialog-bg
      + * - div            Dialog area              modal-dialog
      + *     - div        Title bar                modal-dialog-title
      + *        - span                             modal-dialog-title-text
      + *          - text  Title text               N/A
      + *        - span                             modal-dialog-title-close
      + *          - ??    Close box                N/A
      + *     - div        Content area             modal-dialog-content
      + *        - ??      User specified content   N/A
      + *     - div        Button area              modal-dialog-buttons
      + *        - button                           N/A
      + *        - button
      + *        - ...
      + * 
      + * @constructor + * @param {string=} opt_class CSS class name for the dialog element, also used + * as a class name prefix for related elements; defaults to modal-dialog. + * This should be a single, valid CSS class name. + * @param {boolean=} opt_useIframeMask Work around windowed controls z-index + * issue by using an iframe instead of a div for bg element. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper; see {@link + * goog.ui.Component} for semantics. + * @extends {goog.ui.ModalPopup} + */ +goog.ui.Dialog = function(opt_class, opt_useIframeMask, opt_domHelper) { + goog.ui.Dialog.base(this, 'constructor', opt_useIframeMask, opt_domHelper); + + /** + * CSS class name for the dialog element, also used as a class name prefix for + * related elements. Defaults to goog.getCssName('modal-dialog'). + * @type {string} + * @private + */ + this.class_ = opt_class || goog.getCssName('modal-dialog'); + + this.buttons_ = goog.ui.Dialog.ButtonSet.createOkCancel(); +}; +goog.inherits(goog.ui.Dialog, goog.ui.ModalPopup); +goog.tagUnsealableClass(goog.ui.Dialog); + + +/** + * Button set. Default to Ok/Cancel. + * @type {goog.ui.Dialog.ButtonSet} + * @private + */ +goog.ui.Dialog.prototype.buttons_; + + +/** + * Whether the escape key closes this dialog. + * @type {boolean} + * @private + */ +goog.ui.Dialog.prototype.escapeToCancel_ = true; + + +/** + * Whether this dialog should include a title close button. + * @type {boolean} + * @private + */ +goog.ui.Dialog.prototype.hasTitleCloseButton_ = true; + + +/** + * Whether the dialog is modal. Defaults to true. + * @type {boolean} + * @private + */ +goog.ui.Dialog.prototype.modal_ = true; + + +/** + * Whether the dialog is draggable. Defaults to true. + * @type {boolean} + * @private + */ +goog.ui.Dialog.prototype.draggable_ = true; + + +/** + * Opacity for background mask. Defaults to 50%. + * @type {number} + * @private + */ +goog.ui.Dialog.prototype.backgroundElementOpacity_ = 0.50; + + +/** + * Dialog's title. + * @type {string} + * @private + */ +goog.ui.Dialog.prototype.title_ = ''; + + +/** + * Dialog's content (HTML). + * @type {?goog.html.SafeHtml} + * @private + */ +goog.ui.Dialog.prototype.content_ = null; + + +/** + * Dragger. + * @type {?goog.fx.Dragger} + * @private + */ +goog.ui.Dialog.prototype.dragger_ = null; + + +/** + * Whether the dialog should be disposed when it is hidden. + * @type {boolean} + * @private + */ +goog.ui.Dialog.prototype.disposeOnHide_ = false; + + +/** + * Element for the title bar. + * @type {?Element} + * @private + */ +goog.ui.Dialog.prototype.titleEl_ = null; + + +/** + * Element for the text area of the title bar. + * @type {?Element} + * @private + */ +goog.ui.Dialog.prototype.titleTextEl_ = null; + + +/** + * Id of element for the text area of the title bar. + * @type {?string} + * @private + */ +goog.ui.Dialog.prototype.titleTextId_ = null; + + +/** + * Element for the close box area of the title bar. + * @type {?Element} + * @private + */ +goog.ui.Dialog.prototype.titleCloseEl_ = null; + + +/** + * Element for the content area. + * @type {?Element} + * @private + */ +goog.ui.Dialog.prototype.contentEl_ = null; + + +/** + * Element for the button bar. + * @type {?Element} + * @private + */ +goog.ui.Dialog.prototype.buttonEl_ = null; + + +/** + * The dialog's preferred ARIA role. + * @type {goog.a11y.aria.Role} + * @private + */ +goog.ui.Dialog.prototype.preferredAriaRole_ = goog.a11y.aria.Role.DIALOG; + + +/** @override */ +goog.ui.Dialog.prototype.getCssClass = function() { + return this.class_; +}; + + +/** + * Sets the title. + * @param {string} title The title text. + */ +goog.ui.Dialog.prototype.setTitle = function(title) { + this.title_ = title; + if (this.titleTextEl_) { + goog.dom.setTextContent(this.titleTextEl_, title); + } +}; + + +/** + * Gets the title. + * @return {string} The title. + */ +goog.ui.Dialog.prototype.getTitle = function() { + return this.title_; +}; + + +/** + * Allows plain text to be set in the content element. + * @param {string} text Content plain text. Newlines are preserved. + */ +goog.ui.Dialog.prototype.setTextContent = function(text) { + this.setSafeHtmlContent( + goog.html.SafeHtml.htmlEscapePreservingNewlines(text)); +}; + + +/** + * Allows arbitrary HTML to be set in the content element. + * @param {!goog.html.SafeHtml} html Content HTML. + */ +goog.ui.Dialog.prototype.setSafeHtmlContent = function(html) { + this.content_ = html; + if (this.contentEl_) { + goog.dom.safe.setInnerHtml(this.contentEl_, html); + } +}; + + +/** + * Gets the content HTML of the content element as a plain string. + * + * Note that this method returns the HTML markup that was previously set via + * setSafeHtmlContent() or setTextContent(). In particular, the HTML returned by + * this method does not reflect any changes to the content element's DOM that + * were made by other means. + * + * @return {string} Content HTML. + */ +goog.ui.Dialog.prototype.getContent = function() { + return this.content_ != null ? goog.html.SafeHtml.unwrap(this.content_) : ''; +}; + + +/** + * Gets the content HTML of the content element. + * @return {goog.html.SafeHtml} Content HTML. + */ +goog.ui.Dialog.prototype.getSafeHtmlContent = function() { + return this.content_; +}; + + +/** + * Returns the dialog's preferred ARIA role. This can be used to override the + * default dialog role, e.g. with an ARIA role of ALERTDIALOG for a simple + * warning or confirmation dialog. + * @return {goog.a11y.aria.Role} This dialog's preferred ARIA role. + */ +goog.ui.Dialog.prototype.getPreferredAriaRole = function() { + return this.preferredAriaRole_; +}; + + +/** + * Sets the dialog's preferred ARIA role. This can be used to override the + * default dialog role, e.g. with an ARIA role of ALERTDIALOG for a simple + * warning or confirmation dialog. + * @param {goog.a11y.aria.Role} role This dialog's preferred ARIA role. + */ +goog.ui.Dialog.prototype.setPreferredAriaRole = function(role) { + this.preferredAriaRole_ = role; +}; + + +/** + * Renders if the DOM is not created. + * @private + */ +goog.ui.Dialog.prototype.renderIfNoDom_ = function() { + if (!this.getElement()) { + // TODO(gboyer): Ideally we'd only create the DOM, but many applications + // are requiring this behavior. Eventually, it would be best if the + // element getters could return null if the elements have not been + // created. + this.render(); + } +}; + + +/** + * Returns the content element so that more complicated things can be done with + * the content area. Renders if the DOM is not yet created. Overrides + * {@link goog.ui.Component#getContentElement}. + * @return {Element} The content element. + * @override + */ +goog.ui.Dialog.prototype.getContentElement = function() { + this.renderIfNoDom_(); + return this.contentEl_; +}; + + +/** + * Returns the title element so that more complicated things can be done with + * the title. Renders if the DOM is not yet created. + * @return {Element} The title element. + */ +goog.ui.Dialog.prototype.getTitleElement = function() { + this.renderIfNoDom_(); + return this.titleEl_; +}; + + +/** + * Returns the title text element so that more complicated things can be done + * with the text of the title. Renders if the DOM is not yet created. + * @return {Element} The title text element. + */ +goog.ui.Dialog.prototype.getTitleTextElement = function() { + this.renderIfNoDom_(); + return this.titleTextEl_; +}; + + +/** + * Returns the title close element so that more complicated things can be done + * with the close area of the title. Renders if the DOM is not yet created. + * @return {Element} The close box. + */ +goog.ui.Dialog.prototype.getTitleCloseElement = function() { + this.renderIfNoDom_(); + return this.titleCloseEl_; +}; + + +/** + * Returns the button element so that more complicated things can be done with + * the button area. Renders if the DOM is not yet created. + * @return {Element} The button container element. + */ +goog.ui.Dialog.prototype.getButtonElement = function() { + this.renderIfNoDom_(); + return this.buttonEl_; +}; + + +/** + * Returns the dialog element so that more complicated things can be done with + * the dialog box. Renders if the DOM is not yet created. + * @return {Element} The dialog element. + */ +goog.ui.Dialog.prototype.getDialogElement = function() { + this.renderIfNoDom_(); + return this.getElement(); +}; + + +/** + * Returns the background mask element so that more complicated things can be + * done with the background region. Renders if the DOM is not yet created. + * @return {Element} The background mask element. + * @override + */ +goog.ui.Dialog.prototype.getBackgroundElement = function() { + this.renderIfNoDom_(); + return goog.ui.Dialog.base(this, 'getBackgroundElement'); +}; + + +/** + * Gets the opacity of the background mask. + * @return {number} Background mask opacity. + */ +goog.ui.Dialog.prototype.getBackgroundElementOpacity = function() { + return this.backgroundElementOpacity_; +}; + + +/** + * Sets the opacity of the background mask. + * @param {number} opacity Background mask opacity. + */ +goog.ui.Dialog.prototype.setBackgroundElementOpacity = function(opacity) { + this.backgroundElementOpacity_ = opacity; + + if (this.getElement()) { + var bgEl = this.getBackgroundElement(); + if (bgEl) { + goog.style.setOpacity(bgEl, this.backgroundElementOpacity_); + } + } +}; + + +/** + * Sets the modal property of the dialog. In case the dialog is already + * inDocument, renders the modal background elements according to the specified + * modal parameter. + * + * Note that non-modal dialogs cannot use an iframe mask. + * + * @param {boolean} modal Whether the dialog is modal. + */ +goog.ui.Dialog.prototype.setModal = function(modal) { + if (modal != this.modal_) { + this.setModalInternal_(modal); + } +}; + + +/** + * Sets the modal property of the dialog. + * @param {boolean} modal Whether the dialog is modal. + * @private + */ +goog.ui.Dialog.prototype.setModalInternal_ = function(modal) { + this.modal_ = modal; + if (this.isInDocument()) { + var dom = this.getDomHelper(); + var bg = this.getBackgroundElement(); + var bgIframe = this.getBackgroundIframe(); + if (modal) { + if (bgIframe) { + dom.insertSiblingBefore(bgIframe, this.getElement()); + } + dom.insertSiblingBefore(bg, this.getElement()); + } else { + dom.removeNode(bgIframe); + dom.removeNode(bg); + } + } + if (this.isVisible()) { + this.setA11YDetectBackground(modal); + } +}; + + +/** + * @return {boolean} modal Whether the dialog is modal. + */ +goog.ui.Dialog.prototype.getModal = function() { + return this.modal_; +}; + + +/** + * @return {string} The CSS class name for the dialog element. + */ +goog.ui.Dialog.prototype.getClass = function() { + return this.getCssClass(); +}; + + +/** + * Sets whether the dialog can be dragged. + * @param {boolean} draggable Whether the dialog can be dragged. + */ +goog.ui.Dialog.prototype.setDraggable = function(draggable) { + this.draggable_ = draggable; + this.setDraggingEnabled_(draggable && this.isInDocument()); +}; + + +/** + * Returns a dragger for moving the dialog and adds a class for the move cursor. + * Defaults to allow dragging of the title only, but can be overridden if + * different drag targets or dragging behavior is desired. + * @return {!goog.fx.Dragger} The created dragger instance. + * @protected + */ +goog.ui.Dialog.prototype.createDragger = function() { + return new goog.fx.Dragger(this.getElement(), this.titleEl_); +}; + + +/** + * @return {boolean} Whether the dialog is draggable. + */ +goog.ui.Dialog.prototype.getDraggable = function() { + return this.draggable_; +}; + + +/** + * Enables or disables dragging. + * @param {boolean} enabled Whether to enable it. + * @private + */ +goog.ui.Dialog.prototype.setDraggingEnabled_ = function(enabled) { + // This isn't ideal, but the quickest and easiest way to append + // title-draggable to the last class in the class_ string, then trim and + // split the string into an array (in case the dialog was set up with + // multiple, space-separated class names). + var classNames = + goog.string.trim(goog.getCssName(this.class_, 'title-draggable')) + .split(' '); + + if (this.getElement()) { + if (enabled) { + goog.dom.classlist.addAll(goog.asserts.assert(this.titleEl_), classNames); + } else { + goog.dom.classlist.removeAll( + goog.asserts.assert(this.titleEl_), classNames); + } + } + + if (enabled && !this.dragger_) { + this.dragger_ = this.createDragger(); + goog.dom.classlist.addAll(goog.asserts.assert(this.titleEl_), classNames); + goog.events.listen( + this.dragger_, goog.fx.Dragger.EventType.START, this.setDraggerLimits_, + false, this); + } else if (!enabled && this.dragger_) { + this.dragger_.dispose(); + this.dragger_ = null; + } +}; + + +/** @override */ +goog.ui.Dialog.prototype.createDom = function() { + goog.ui.Dialog.base(this, 'createDom'); + var element = this.getElement(); + goog.asserts.assert(element, 'getElement() returns null'); + + var dom = this.getDomHelper(); + this.titleEl_ = dom.createDom( + goog.dom.TagName.DIV, goog.getCssName(this.class_, 'title'), + this.titleTextEl_ = dom.createDom( + goog.dom.TagName.SPAN, { + 'className': goog.getCssName(this.class_, 'title-text'), + 'id': this.getId() + }, + this.title_), + this.titleCloseEl_ = dom.createDom( + goog.dom.TagName.SPAN, goog.getCssName(this.class_, 'title-close'))), + goog.dom.append( + element, this.titleEl_, + this.contentEl_ = dom.createDom( + goog.dom.TagName.DIV, goog.getCssName(this.class_, 'content')), + this.buttonEl_ = dom.createDom( + goog.dom.TagName.DIV, goog.getCssName(this.class_, 'buttons'))); + + // Make the title and close button behave correctly with screen readers. + // Note: this is only being added if the dialog is not decorated. Decorators + // are expected to add aria label, role, and tab indexing in their templates. + goog.a11y.aria.setRole(this.titleTextEl_, goog.a11y.aria.Role.HEADING); + goog.a11y.aria.setRole(this.titleCloseEl_, goog.a11y.aria.Role.BUTTON); + goog.dom.setFocusableTabIndex(this.titleCloseEl_, true); + goog.a11y.aria.setLabel( + this.titleCloseEl_, goog.ui.Dialog.MSG_GOOG_UI_DIALOG_CLOSE_); + + this.titleTextId_ = this.titleTextEl_.id; + goog.a11y.aria.setRole(element, this.getPreferredAriaRole()); + goog.a11y.aria.setState( + element, goog.a11y.aria.State.LABELLEDBY, this.titleTextId_ || ''); + // If setContent() was called before createDom(), make sure the inner HTML of + // the content element is initialized. + if (this.content_) { + goog.dom.safe.setInnerHtml(this.contentEl_, this.content_); + } + goog.style.setElementShown(this.titleCloseEl_, this.hasTitleCloseButton_); + + // Render the buttons. + if (this.buttons_) { + this.buttons_.attachToElement(this.buttonEl_); + } + goog.style.setElementShown(this.buttonEl_, !!this.buttons_); + this.setBackgroundElementOpacity(this.backgroundElementOpacity_); +}; + + +/** @override */ +goog.ui.Dialog.prototype.decorateInternal = function(element) { + goog.ui.Dialog.base(this, 'decorateInternal', element); + var dialogElement = this.getElement(); + goog.asserts.assert( + dialogElement, 'The DOM element for dialog cannot be null.'); + // Decorate or create the content element. + var contentClass = goog.getCssName(this.class_, 'content'); + this.contentEl_ = goog.dom.getElementsByTagNameAndClass( + null, contentClass, dialogElement)[0]; + if (!this.contentEl_) { + this.contentEl_ = + this.getDomHelper().createDom(goog.dom.TagName.DIV, contentClass); + if (this.content_) { + goog.dom.safe.setInnerHtml(this.contentEl_, this.content_); + } + dialogElement.appendChild(this.contentEl_); + } + + // Decorate or create the title bar element. + var titleClass = goog.getCssName(this.class_, 'title'); + var titleTextClass = goog.getCssName(this.class_, 'title-text'); + var titleCloseClass = goog.getCssName(this.class_, 'title-close'); + this.titleEl_ = + goog.dom.getElementsByTagNameAndClass(null, titleClass, dialogElement)[0]; + if (this.titleEl_) { + // Only look for title text & title close elements if a title bar element + // was found. Otherwise assume that the entire title bar has to be + // created from scratch. + this.titleTextEl_ = goog.dom.getElementsByTagNameAndClass( + null, titleTextClass, this.titleEl_)[0]; + this.titleCloseEl_ = goog.dom.getElementsByTagNameAndClass( + null, titleCloseClass, this.titleEl_)[0]; + } else { + // Create the title bar element and insert it before the content area. + // This is useful if the element to decorate only includes a content area. + this.titleEl_ = + this.getDomHelper().createDom(goog.dom.TagName.DIV, titleClass); + dialogElement.insertBefore(this.titleEl_, this.contentEl_); + } + + // Decorate or create the title text element. + if (this.titleTextEl_) { + this.title_ = goog.dom.getTextContent(this.titleTextEl_); + // Give the title text element an id if it doesn't already have one. + if (!this.titleTextEl_.id) { + this.titleTextEl_.id = this.getId(); + } + } else { + this.titleTextEl_ = goog.dom.createDom( + goog.dom.TagName.SPAN, + {'className': titleTextClass, 'id': this.getId()}); + this.titleEl_.appendChild(this.titleTextEl_); + } + this.titleTextId_ = this.titleTextEl_.id; + goog.a11y.aria.setState( + dialogElement, goog.a11y.aria.State.LABELLEDBY, this.titleTextId_ || ''); + // Decorate or create the title close element. + if (!this.titleCloseEl_) { + this.titleCloseEl_ = + this.getDomHelper().createDom(goog.dom.TagName.SPAN, titleCloseClass); + this.titleEl_.appendChild(this.titleCloseEl_); + } + goog.style.setElementShown(this.titleCloseEl_, this.hasTitleCloseButton_); + + // Decorate or create the button container element. + var buttonsClass = goog.getCssName(this.class_, 'buttons'); + this.buttonEl_ = goog.dom.getElementsByTagNameAndClass( + null, buttonsClass, dialogElement)[0]; + if (this.buttonEl_) { + // Button container element found. Create empty button set and use it to + // decorate the button container. + this.buttons_ = new goog.ui.Dialog.ButtonSet(this.getDomHelper()); + this.buttons_.decorate(this.buttonEl_); + } else { + // Create new button container element, and render a button set into it. + this.buttonEl_ = + this.getDomHelper().createDom(goog.dom.TagName.DIV, buttonsClass); + dialogElement.appendChild(this.buttonEl_); + if (this.buttons_) { + this.buttons_.attachToElement(this.buttonEl_); + } + goog.style.setElementShown(this.buttonEl_, !!this.buttons_); + } + this.setBackgroundElementOpacity(this.backgroundElementOpacity_); +}; + + +/** @override */ +goog.ui.Dialog.prototype.enterDocument = function() { + goog.ui.Dialog.base(this, 'enterDocument'); + + // Listen for keyboard events while the dialog is visible. + this.getHandler() + .listen(this.getElement(), goog.events.EventType.KEYDOWN, this.onKey_) + .listen(this.getElement(), goog.events.EventType.KEYPRESS, this.onKey_); + + // NOTE: see bug 1163154 for an example of an edge case where making the + // dialog visible in response to a KEYDOWN will result in a CLICK event + // firing on the default button (immediately closing the dialog) if the key + // that fired the KEYDOWN is also normally used to activate controls + // (i.e. SPACE/ENTER). + // + // This could be worked around by attaching the onButtonClick_ handler in a + // setTimeout, but that was deemed undesirable. + this.getHandler().listen( + this.buttonEl_, goog.events.EventType.CLICK, this.onButtonClick_); + + // Add drag support. + this.setDraggingEnabled_(this.draggable_); + + // Add event listeners to the close box and the button container. + this.getHandler().listen( + this.titleCloseEl_, goog.events.EventType.CLICK, this.onTitleCloseClick_); + + var element = this.getElement(); + goog.asserts.assert(element, 'The DOM element for dialog cannot be null'); + goog.a11y.aria.setRole(element, this.getPreferredAriaRole()); + if (this.titleTextEl_.id !== '') { + goog.a11y.aria.setState( + element, goog.a11y.aria.State.LABELLEDBY, this.titleTextEl_.id); + } + + if (!this.modal_) { + this.setModalInternal_(false); + } +}; + + +/** @override */ +goog.ui.Dialog.prototype.exitDocument = function() { + if (this.isVisible()) { + this.setVisible(false); + } + + // Remove drag support. + this.setDraggingEnabled_(false); + + goog.ui.Dialog.base(this, 'exitDocument'); +}; + + +/** + * Sets the visibility of the dialog box and moves focus to the + * default button. Lazily renders the component if needed. After this + * method returns, isVisible() will always return the new state, even + * if there is a transition. + * @param {boolean} visible Whether the dialog should be visible. + * @override + */ +goog.ui.Dialog.prototype.setVisible = function(visible) { + if (visible == this.isVisible()) { + return; + } + + // If the dialog hasn't been rendered yet, render it now. + if (!this.isInDocument()) { + this.render(); + } + + goog.ui.Dialog.base(this, 'setVisible', visible); +}; + + +/** + * @override + * @suppress {deprecated} AFTER_SHOW is deprecated earlier in this file. + */ +goog.ui.Dialog.prototype.onShow = function() { + goog.ui.Dialog.base(this, 'onShow'); + this.dispatchEvent(goog.ui.Dialog.EventType.AFTER_SHOW); +}; + + +/** + * @override + * @suppress {deprecated} AFTER_HIDE is deprecated earlier in this file. + */ +goog.ui.Dialog.prototype.onHide = function() { + goog.ui.Dialog.base(this, 'onHide'); + this.dispatchEvent(goog.ui.Dialog.EventType.AFTER_HIDE); + if (this.disposeOnHide_) { + this.dispose(); + } +}; + + +/** + * Sets dragger limits when dragging is started. + * @param {!goog.events.Event} e goog.fx.Dragger.EventType.START event. + * @private + */ +goog.ui.Dialog.prototype.setDraggerLimits_ = function(e) { + var doc = this.getDomHelper().getDocument(); + var win = goog.dom.getWindow(doc) || window; + + // Take the max of scroll height and view height for cases in which document + // does not fill screen. + var viewSize = goog.dom.getViewportSize(win); + var w = Math.max(doc.body.scrollWidth, viewSize.width); + var h = Math.max(doc.body.scrollHeight, viewSize.height); + + var dialogSize = goog.style.getSize(this.getElement()); + if (goog.style.getComputedPosition(this.getElement()) == 'fixed') { + // Ensure position:fixed dialogs can't be dragged beyond the viewport. + this.dragger_.setLimits( + new goog.math.Rect( + 0, 0, Math.max(0, viewSize.width - dialogSize.width), + Math.max(0, viewSize.height - dialogSize.height))); + } else { + this.dragger_.setLimits( + new goog.math.Rect(0, 0, w - dialogSize.width, h - dialogSize.height)); + } +}; + + +/** + * Handles a click on the title close area. + * @param {goog.events.BrowserEvent} e Browser's event object. + * @private + */ +goog.ui.Dialog.prototype.onTitleCloseClick_ = function(e) { + this.handleTitleClose_(); +}; + + +/** + * Performs the action of closing the dialog in response to the title close + * button being interacted with. General purpose method to be called by click + * and button event handlers. + * @private + */ +goog.ui.Dialog.prototype.handleTitleClose_ = function() { + if (!this.hasTitleCloseButton_) { + return; + } + + var bs = this.getButtonSet(); + var key = bs && bs.getCancel(); + // Only if there is a valid cancel button is an event dispatched. + if (key) { + var caption = /** @type {Element|string} */ (bs.get(key)); + if (this.dispatchEvent(new goog.ui.Dialog.Event(key, caption))) { + this.setVisible(false); + } + } else { + this.setVisible(false); + } +}; + + +/** + * @return {boolean} Whether this dialog has a title close button. + */ +goog.ui.Dialog.prototype.getHasTitleCloseButton = function() { + return this.hasTitleCloseButton_; +}; + + +/** + * Sets whether the dialog should have a close button in the title bar. There + * will always be an element for the title close button, but setting this + * parameter to false will cause it to be hidden and have no active listener. + * @param {boolean} b Whether this dialog should have a title close button. + */ +goog.ui.Dialog.prototype.setHasTitleCloseButton = function(b) { + this.hasTitleCloseButton_ = b; + if (this.titleCloseEl_) { + goog.style.setElementShown(this.titleCloseEl_, this.hasTitleCloseButton_); + } +}; + + +/** + * @return {boolean} Whether the escape key should close this dialog. + */ +goog.ui.Dialog.prototype.isEscapeToCancel = function() { + return this.escapeToCancel_; +}; + + +/** + * @param {boolean} b Whether the escape key should close this dialog. + */ +goog.ui.Dialog.prototype.setEscapeToCancel = function(b) { + this.escapeToCancel_ = b; +}; + + +/** + * Sets whether the dialog should be disposed when it is hidden. By default + * dialogs are not disposed when they are hidden. + * @param {boolean} b Whether the dialog should get disposed when it gets + * hidden. + */ +goog.ui.Dialog.prototype.setDisposeOnHide = function(b) { + this.disposeOnHide_ = b; +}; + + +/** + * @return {boolean} Whether the dialog should be disposed when it is hidden. + */ +goog.ui.Dialog.prototype.getDisposeOnHide = function() { + return this.disposeOnHide_; +}; + + +/** @override */ +goog.ui.Dialog.prototype.disposeInternal = function() { + this.titleCloseEl_ = null; + this.buttonEl_ = null; + goog.ui.Dialog.base(this, 'disposeInternal'); +}; + + +/** + * Sets the button set to use. + * Note: Passing in null will cause no button set to be rendered. + * @param {goog.ui.Dialog.ButtonSet?} buttons The button set to use. + */ +goog.ui.Dialog.prototype.setButtonSet = function(buttons) { + this.buttons_ = buttons; + if (this.buttonEl_) { + if (this.buttons_) { + this.buttons_.attachToElement(this.buttonEl_); + } else { + goog.dom.safe.setInnerHtml(this.buttonEl_, goog.html.SafeHtml.EMPTY); + } + goog.style.setElementShown(this.buttonEl_, !!this.buttons_); + } +}; + + +/** + * Returns the button set being used. + * @return {goog.ui.Dialog.ButtonSet?} The button set being used. + */ +goog.ui.Dialog.prototype.getButtonSet = function() { + return this.buttons_; +}; + + +/** + * Handles a click on the button container. + * @param {goog.events.BrowserEvent} e Browser's event object. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.Dialog.prototype.onButtonClick_ = function(e) { + var button = this.findParentButton_(/** @type {Element} */ (e.target)); + if (button && !button.disabled) { + var key = button.name; + var caption = /** @type {Element|string} */ (this.getButtonSet().get(key)); + if (this.dispatchEvent(new goog.ui.Dialog.Event(key, caption))) { + this.setVisible(false); + } + } +}; + + +/** + * Finds the parent button of an element (or null if there was no button + * parent). + * @param {Element} element The element that was clicked on. + * @return {Element} Returns the parent button or null if not found. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.Dialog.prototype.findParentButton_ = function(element) { + var el = element; + while (el != null && el != this.buttonEl_) { + if (el.tagName == goog.dom.TagName.BUTTON) { + return /** @type {Element} */ (el); + } + el = el.parentNode; + } + return null; +}; + + +/** + * Handles keydown and keypress events, and dismisses the popup if cancel is + * pressed. If there is a cancel action in the ButtonSet, than that will be + * fired. Also prevents tabbing out of the dialog. + * @param {goog.events.BrowserEvent} e Browser's event object. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.Dialog.prototype.onKey_ = function(e) { + var close = false; + var hasHandler = false; + var buttonSet = this.getButtonSet(); + var target = e.target; + + if (e.type == goog.events.EventType.KEYDOWN) { + // Escape and tab can only properly be handled in keydown handlers. + if (this.escapeToCancel_ && e.keyCode == goog.events.KeyCodes.ESC) { + // Only if there is a valid cancel button is an event dispatched. + var cancel = buttonSet && buttonSet.getCancel(); + + // Users may expect to hit escape on a SELECT element. + var isSpecialFormElement = + target.tagName == goog.dom.TagName.SELECT && !target.disabled; + + if (cancel && !isSpecialFormElement) { + hasHandler = true; + + var caption = buttonSet.get(cancel); + close = this.dispatchEvent( + new goog.ui.Dialog.Event( + cancel, + /** @type {Element|null|string} */ (caption))); + } else if (!isSpecialFormElement) { + close = true; + } + } else if ( + e.keyCode == goog.events.KeyCodes.TAB && e.shiftKey && + target == this.getElement()) { + // Prevent the user from shift-tabbing backwards out of the dialog box. + // Instead, set up a wrap in focus backward to the end of the dialog. + this.setupBackwardTabWrap(); + } + } else if (e.keyCode == goog.events.KeyCodes.ENTER) { + // Only handle ENTER in keypress events, in case the action opens a + // popup window. + var key; + if (target.tagName == goog.dom.TagName.BUTTON && !target.disabled) { + // If the target is a button and it's enabled, we can fire that button's + // handler. + key = target.name; + } else if (target == this.titleCloseEl_) { + // if the title 'close' button is in focus, close the dialog + this.handleTitleClose_(); + } else if (buttonSet) { + // Try to fire the default button's handler (if one exists), but only if + // the button is enabled. + var defaultKey = buttonSet.getDefault(); + var defaultButton = defaultKey && buttonSet.getButton(defaultKey); + + // Users may expect to hit enter on a TEXTAREA, SELECT or an A element. + var isSpecialFormElement = (target.tagName == goog.dom.TagName.TEXTAREA || + target.tagName == goog.dom.TagName.SELECT || + target.tagName == goog.dom.TagName.A) && + !target.disabled; + + if (defaultButton && !defaultButton.disabled && !isSpecialFormElement) { + key = defaultKey; + } + } + if (key && buttonSet) { + hasHandler = true; + close = this.dispatchEvent( + new goog.ui.Dialog.Event(key, String(buttonSet.get(key)))); + } + } else if ( + target == this.titleCloseEl_ && + (e.keyCode == goog.events.KeyCodes.SPACE || + e.key == goog.events.Keys.SPACE)) { + // if the title 'close' button is in focus on 'SPACE,' close the dialog + this.handleTitleClose_(); + } + + if (close || hasHandler) { + e.stopPropagation(); + e.preventDefault(); + } + + if (close) { + this.setVisible(false); + } +}; + + + +/** + * Dialog event class. + * @param {string} key Key identifier for the button. + * @param {string|Element} caption Caption on the button (might be i18nlized). + * @constructor + * @extends {goog.events.Event} + */ +goog.ui.Dialog.Event = function(key, caption) { + /** @const {!goog.ui.Dialog.EventType} */ + this.type = goog.ui.Dialog.EventType.SELECT; + /** @const */ + this.key = key; + /** @const */ + this.caption = caption; +}; +goog.inherits(goog.ui.Dialog.Event, goog.events.Event); + + +/** + * Event type constant for dialog events. + * TODO(attila): Change this to goog.ui.Dialog.EventType.SELECT. + * @type {string} + * @deprecated Use goog.ui.Dialog.EventType.SELECT. + */ +goog.ui.Dialog.SELECT_EVENT = 'dialogselect'; + + +/** + * Events dispatched by dialogs. + * @enum {string} + */ +goog.ui.Dialog.EventType = { + /** + * Dispatched when the user closes the dialog. + * The dispatched event will always be of type {@link goog.ui.Dialog.Event}. + * Canceling the event will prevent the dialog from closing. + */ + SELECT: 'dialogselect', + + /** + * Dispatched after the dialog is closed. Not cancelable. + * @deprecated Use goog.ui.PopupBase.EventType.HIDE. + */ + AFTER_HIDE: 'afterhide', + + /** + * Dispatched after the dialog is shown. Not cancelable. + * @deprecated Use goog.ui.PopupBase.EventType.SHOW. + */ + AFTER_SHOW: 'aftershow' +}; + + + +/** + * A button set defines the behaviour of a set of buttons that the dialog can + * show. Uses the {@link goog.structs.Map} interface. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper; see {@link + * goog.ui.Component} for semantics. + * @constructor + * @extends {goog.structs.Map} + * @suppress {deprecated} Underlying extended goog.structs.Map is deprecated but + * this class is not. Suppress warnings until refactored. + */ +goog.ui.Dialog.ButtonSet = function(opt_domHelper) { + // TODO(attila): Refactor ButtonSet to extend goog.ui.Component? + this.dom_ = opt_domHelper || goog.dom.getDomHelper(); + goog.structs.Map.call(this); +}; +goog.inherits(goog.ui.Dialog.ButtonSet, goog.structs.Map); +goog.tagUnsealableClass(goog.ui.Dialog.ButtonSet); + + +/** + * A CSS className for this component. + * @type {string} + * @private + */ +goog.ui.Dialog.ButtonSet.prototype.class_ = goog.getCssName('goog-buttonset'); + + +/** + * The button that has default focus (references key in buttons_ map). + * @type {?string} + * @private + */ +goog.ui.Dialog.ButtonSet.prototype.defaultButton_ = null; + + +/** + * Optional container the button set should be rendered into. + * @type {?Element} + * @private + */ +goog.ui.Dialog.ButtonSet.prototype.element_ = null; + + +/** + * The button whose action is associated with the escape key and the X button + * on the dialog. + * @type {?string} + * @private + */ +goog.ui.Dialog.ButtonSet.prototype.cancelButton_ = null; + + +/** @override */ +goog.ui.Dialog.ButtonSet.prototype.clear = function() { + goog.structs.Map.prototype.clear.call(this); + this.defaultButton_ = this.cancelButton_ = null; +}; + + +/** + * Adds a button to the button set. Buttons will be displayed in the order they + * are added. + * + * @param {*} key Key used to identify the button in events. + * @param {*} caption A string caption or a DOM node that can be + * appended to a button element. + * @param {boolean=} opt_isDefault Whether this button is the default button, + * Dialog will dispatch for this button if enter is pressed. + * @param {boolean=} opt_isCancel Whether this button has the same behaviour as + * cancel. If escape is pressed this button will fire. + * @return {!goog.ui.Dialog.ButtonSet} The button set, to make it easy to chain + * "set" calls and build new ButtonSets. + * @override + */ +goog.ui.Dialog.ButtonSet.prototype.set = function( + key, caption, opt_isDefault, opt_isCancel) { + goog.structs.Map.prototype.set.call(this, key, caption); + + if (opt_isDefault) { + this.defaultButton_ = /** @type {?string} */ (key); + } + if (opt_isCancel) { + this.cancelButton_ = /** @type {?string} */ (key); + } + + return this; +}; + + +/** + * Adds a button (an object with a key and caption) to this button set. Buttons + * will be displayed in the order they are added. + * @see goog.ui.Dialog.DefaultButtons + * @param {{key: string, caption: string}} button The button key and caption. + * @param {boolean=} opt_isDefault Whether this button is the default button. + * Dialog will dispatch for this button if enter is pressed. + * @param {boolean=} opt_isCancel Whether this button has the same behavior as + * cancel. If escape is pressed this button will fire. + * @return {!goog.ui.Dialog.ButtonSet} The button set, to make it easy to chain + * "addButton" calls and build new ButtonSets. + */ +goog.ui.Dialog.ButtonSet.prototype.addButton = function( + button, opt_isDefault, opt_isCancel) { + return this.set(button.key, button.caption, opt_isDefault, opt_isCancel); +}; + + +/** + * Attaches the button set to an element, rendering it inside. + * @param {Element} el Container. + */ +goog.ui.Dialog.ButtonSet.prototype.attachToElement = function(el) { + this.element_ = el; + this.render(); +}; + + +/** + * Renders the button set inside its container element. + */ +goog.ui.Dialog.ButtonSet.prototype.render = function() { + if (this.element_) { + goog.dom.safe.setInnerHtml(this.element_, goog.html.SafeHtml.EMPTY); + var domHelper = goog.dom.getDomHelper(this.element_); + this.forEach(function(caption, key) { + var button = + domHelper.createDom(goog.dom.TagName.BUTTON, {'name': key}, caption); + if (key == this.defaultButton_) { + button.className = goog.getCssName(this.class_, 'default'); + } + this.element_.appendChild(button); + }, this); + } +}; + + +/** + * Decorates the given element by adding any `button` elements found + * among its descendants to the button set. The first button found is assumed + * to be the default and will receive focus when the button set is rendered. + * If a button with a name of {@link goog.ui.Dialog.DefaultButtonKeys.CANCEL} + * is found, it is assumed to have "Cancel" semantics. + * TODO(attila): ButtonSet should be a goog.ui.Component. Really. + * @param {Element} element The element to decorate; should contain buttons. + */ +goog.ui.Dialog.ButtonSet.prototype.decorate = function(element) { + if (!element || element.nodeType != goog.dom.NodeType.ELEMENT) { + return; + } + + this.element_ = element; + var buttons = + goog.dom.getElementsByTagName(goog.dom.TagName.BUTTON, this.element_); + for (var i = 0, button, key, caption; button = buttons[i]; i++) { + // Buttons should have a "name" attribute and have their caption defined by + // their innerHTML, but not everyone knows this, and we should play nice. + key = button.name || button.id; + caption = goog.dom.getTextContent(button) || button.value; + if (key) { + var isDefault = i == 0; + var isCancel = button.name == goog.ui.Dialog.DefaultButtonKeys.CANCEL; + this.set(key, caption, isDefault, isCancel); + if (isDefault) { + goog.dom.classlist.add(button, goog.getCssName(this.class_, 'default')); + } + } + } +}; + + +/** + * Gets the component's element. + * @return {Element} The element for the component. + * TODO(user): Remove after refactoring to goog.ui.Component. + */ +goog.ui.Dialog.ButtonSet.prototype.getElement = function() { + return this.element_; +}; + + +/** + * Returns the dom helper that is being used on this component. + * @return {!goog.dom.DomHelper} The dom helper used on this component. + * TODO(user): Remove after refactoring to goog.ui.Component. + */ +goog.ui.Dialog.ButtonSet.prototype.getDomHelper = function() { + return this.dom_; +}; + + +/** + * Sets the default button. + * @param {?string} key The default button. + */ +goog.ui.Dialog.ButtonSet.prototype.setDefault = function(key) { + this.defaultButton_ = key; +}; + + +/** + * Returns the default button. + * @return {?string} The default button. + */ +goog.ui.Dialog.ButtonSet.prototype.getDefault = function() { + return this.defaultButton_; +}; + + +/** + * Sets the cancel button. + * @param {?string} key The cancel button. + */ +goog.ui.Dialog.ButtonSet.prototype.setCancel = function(key) { + this.cancelButton_ = key; +}; + + +/** + * Returns the cancel button. + * @return {?string} The cancel button. + */ +goog.ui.Dialog.ButtonSet.prototype.getCancel = function() { + return this.cancelButton_; +}; + + +/** + * Returns the HTML Button element. + * @param {string} key The button to return. + * @return {Element} The button, if found else null. + */ +goog.ui.Dialog.ButtonSet.prototype.getButton = function(key) { + var buttons = this.getAllButtons(); + for (var i = 0, nextButton; nextButton = buttons[i]; i++) { + if (nextButton.name == key || nextButton.id == key) { + return nextButton; + } + } + return null; +}; + + +/** + * Returns all the HTML Button elements in the button set container. + * @return {!IArrayLike} A live NodeList of the buttons. + */ +goog.ui.Dialog.ButtonSet.prototype.getAllButtons = function() { + return goog.dom.getElementsByTagName( + goog.dom.TagName.BUTTON, goog.asserts.assert(this.element_)); +}; + + +/** + * Enables or disables a button in this set by key. If the button is not found, + * does nothing. + * @param {string} key The button to enable or disable. + * @param {boolean} enabled True to enable; false to disable. + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.Dialog.ButtonSet.prototype.setButtonEnabled = function(key, enabled) { + var button = this.getButton(key); + if (button) { + button.disabled = !enabled; + } +}; + + +/** + * Enables or disables all of the buttons in this set. + * @param {boolean} enabled True to enable; false to disable. + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.Dialog.ButtonSet.prototype.setAllButtonsEnabled = function(enabled) { + var allButtons = this.getAllButtons(); + for (var i = 0, button; button = allButtons[i]; i++) { + button.disabled = !enabled; + } +}; + + +/** + * The keys used to identify standard buttons in events. + * @enum {string} + */ +goog.ui.Dialog.DefaultButtonKeys = { + OK: 'ok', + CANCEL: 'cancel', + YES: 'yes', + NO: 'no', + SAVE: 'save', + CONTINUE: 'continue' +}; + + +/** + * @desc Standard caption for the dialog 'OK' button. + * @private + */ +goog.ui.Dialog.MSG_DIALOG_OK_ = goog.getMsg('OK'); + + +/** + * @desc Standard caption for the dialog 'Cancel' button. + * @private + */ +goog.ui.Dialog.MSG_DIALOG_CANCEL_ = goog.getMsg('Cancel'); + + +/** + * @desc Standard caption for the dialog 'Yes' button. + * @private + */ +goog.ui.Dialog.MSG_DIALOG_YES_ = goog.getMsg('Yes'); + + +/** + * @desc Standard caption for the dialog 'No' button. + * @private + */ +goog.ui.Dialog.MSG_DIALOG_NO_ = goog.getMsg('No'); + + +/** + * @desc Standard caption for the dialog 'Save' button. + * @private + */ +goog.ui.Dialog.MSG_DIALOG_SAVE_ = goog.getMsg('Save'); + + +/** + * @desc Standard caption for the dialog 'Continue' button. + * @private + */ +goog.ui.Dialog.MSG_DIALOG_CONTINUE_ = goog.getMsg('Continue'); + + +/** + * @desc Standard label for the dialog 'X' (close) button. + * @private + */ +goog.ui.Dialog.MSG_GOOG_UI_DIALOG_CLOSE_ = goog.getMsg('Close'); + + +/** + * The default captions for the default buttons. + * @enum {string} + */ +goog.ui.Dialog.DefaultButtonCaptions = { + OK: goog.ui.Dialog.MSG_DIALOG_OK_, + CANCEL: goog.ui.Dialog.MSG_DIALOG_CANCEL_, + YES: goog.ui.Dialog.MSG_DIALOG_YES_, + NO: goog.ui.Dialog.MSG_DIALOG_NO_, + SAVE: goog.ui.Dialog.MSG_DIALOG_SAVE_, + CONTINUE: goog.ui.Dialog.MSG_DIALOG_CONTINUE_ +}; + + +/** + * The standard buttons (keys associated with captions). + * @enum {{key: string, caption: string}} + */ +goog.ui.Dialog.ButtonSet.DefaultButtons = { + OK: { + key: goog.ui.Dialog.DefaultButtonKeys.OK, + caption: goog.ui.Dialog.DefaultButtonCaptions.OK + }, + CANCEL: { + key: goog.ui.Dialog.DefaultButtonKeys.CANCEL, + caption: goog.ui.Dialog.DefaultButtonCaptions.CANCEL + }, + YES: { + key: goog.ui.Dialog.DefaultButtonKeys.YES, + caption: goog.ui.Dialog.DefaultButtonCaptions.YES + }, + NO: { + key: goog.ui.Dialog.DefaultButtonKeys.NO, + caption: goog.ui.Dialog.DefaultButtonCaptions.NO + }, + SAVE: { + key: goog.ui.Dialog.DefaultButtonKeys.SAVE, + caption: goog.ui.Dialog.DefaultButtonCaptions.SAVE + }, + CONTINUE: { + key: goog.ui.Dialog.DefaultButtonKeys.CONTINUE, + caption: goog.ui.Dialog.DefaultButtonCaptions.CONTINUE + } +}; + + +/** + * Creates a new ButtonSet with a single 'OK' button, which is also set with + * cancel button semantics so that pressing escape will close the dialog. + * @return {!goog.ui.Dialog.ButtonSet} The created ButtonSet. + */ +goog.ui.Dialog.ButtonSet.createOk = function() { + return new goog.ui.Dialog.ButtonSet().addButton( + goog.ui.Dialog.ButtonSet.DefaultButtons.OK, true, true); +}; + + +/** + * Creates a new ButtonSet with 'OK' (default) and 'Cancel' buttons. + * @return {!goog.ui.Dialog.ButtonSet} The created ButtonSet. + */ +goog.ui.Dialog.ButtonSet.createOkCancel = function() { + return new goog.ui.Dialog.ButtonSet() + .addButton(goog.ui.Dialog.ButtonSet.DefaultButtons.OK, true) + .addButton(goog.ui.Dialog.ButtonSet.DefaultButtons.CANCEL, false, true); +}; + + +/** + * Creates a new ButtonSet with 'Yes' (default) and 'No' buttons. + * @return {!goog.ui.Dialog.ButtonSet} The created ButtonSet. + */ +goog.ui.Dialog.ButtonSet.createYesNo = function() { + return new goog.ui.Dialog.ButtonSet() + .addButton(goog.ui.Dialog.ButtonSet.DefaultButtons.YES, true) + .addButton(goog.ui.Dialog.ButtonSet.DefaultButtons.NO, false, true); +}; + + +/** + * Creates a new ButtonSet with 'Yes', 'No' (default), and 'Cancel' buttons. + * @return {!goog.ui.Dialog.ButtonSet} The created ButtonSet. + */ +goog.ui.Dialog.ButtonSet.createYesNoCancel = function() { + return new goog.ui.Dialog.ButtonSet() + .addButton(goog.ui.Dialog.ButtonSet.DefaultButtons.YES) + .addButton(goog.ui.Dialog.ButtonSet.DefaultButtons.NO, true) + .addButton(goog.ui.Dialog.ButtonSet.DefaultButtons.CANCEL, false, true); +}; + + +/** + * Creates a new ButtonSet with 'Continue', 'Save', and 'Cancel' (default) + * buttons. + * @return {!goog.ui.Dialog.ButtonSet} The created ButtonSet. + */ +goog.ui.Dialog.ButtonSet.createContinueSaveCancel = function() { + return new goog.ui.Dialog.ButtonSet() + .addButton(goog.ui.Dialog.ButtonSet.DefaultButtons.CONTINUE) + .addButton(goog.ui.Dialog.ButtonSet.DefaultButtons.SAVE) + .addButton(goog.ui.Dialog.ButtonSet.DefaultButtons.CANCEL, true, true); +}; + + +// TODO(user): These shared instances should be phased out. +(function() { + if (typeof document != 'undefined') { + /** @deprecated Use goog.ui.Dialog.ButtonSet#createOk. */ + goog.ui.Dialog.ButtonSet.OK = goog.ui.Dialog.ButtonSet.createOk(); + + /** @deprecated Use goog.ui.Dialog.ButtonSet#createOkCancel. */ + goog.ui.Dialog.ButtonSet.OK_CANCEL = + goog.ui.Dialog.ButtonSet.createOkCancel(); + + /** @deprecated Use goog.ui.Dialog.ButtonSet#createYesNo. */ + goog.ui.Dialog.ButtonSet.YES_NO = goog.ui.Dialog.ButtonSet.createYesNo(); + + /** @deprecated Use goog.ui.Dialog.ButtonSet#createYesNoCancel. */ + goog.ui.Dialog.ButtonSet.YES_NO_CANCEL = + goog.ui.Dialog.ButtonSet.createYesNoCancel(); + + /** @deprecated Use goog.ui.Dialog.ButtonSet#createContinueSaveCancel. */ + goog.ui.Dialog.ButtonSet.CONTINUE_SAVE_CANCEL = + goog.ui.Dialog.ButtonSet.createContinueSaveCancel(); + } +})(); diff --git a/closure-library/closure/goog/ui/dimensionpicker.js b/closure-library/closure/goog/ui/dimensionpicker.js new file mode 100644 index 0000000000..62837652e3 --- /dev/null +++ b/closure-library/closure/goog/ui/dimensionpicker.js @@ -0,0 +1,389 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A dimension picker control. A dimension picker allows the + * user to visually select a row and column count. + * + * @author robbyw@google.com (Robby Walker) + * @see ../demos/dimensionpicker.html + * @see ../demos/dimensionpicker_rtl.html + */ + +goog.provide('goog.ui.DimensionPicker'); + +goog.require('goog.events.BrowserEvent.PointerType'); +goog.require('goog.events.EventType'); +goog.require('goog.events.KeyCodes'); +goog.require('goog.math.Size'); +goog.require('goog.ui.Component'); +goog.require('goog.ui.ComponentUtil'); +goog.require('goog.ui.Control'); +goog.require('goog.ui.DimensionPickerRenderer'); +goog.require('goog.ui.registry'); + + + +/** + * A dimension picker allows the user to visually select a row and column + * count using their mouse and keyboard. + * + * The currently selected dimension is controlled by an ACTION event. Event + * listeners may retrieve the selected item using the + * {@link #getValue} method. + * + * @param {goog.ui.DimensionPickerRenderer=} opt_renderer Renderer used to + * render or decorate the palette; defaults to + * {@link goog.ui.DimensionPickerRenderer}. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for + * document interaction. + * @constructor + * @extends {goog.ui.Control} + * @final + */ +goog.ui.DimensionPicker = function(opt_renderer, opt_domHelper) { + goog.ui.Control.call( + this, null, opt_renderer || goog.ui.DimensionPickerRenderer.getInstance(), + opt_domHelper); + + this.size_ = new goog.math.Size(this.minColumns, this.minRows); +}; +goog.inherits(goog.ui.DimensionPicker, goog.ui.Control); + + +/** + * Minimum number of columns to show in the grid. + * @type {number} + */ +goog.ui.DimensionPicker.prototype.minColumns = 5; + + +/** + * Minimum number of rows to show in the grid. + * @type {number} + */ +goog.ui.DimensionPicker.prototype.minRows = 5; + + +/** + * Maximum number of columns to show in the grid. + * @type {number} + */ +goog.ui.DimensionPicker.prototype.maxColumns = 20; + + +/** + * Maximum number of rows to show in the grid. + * @type {number} + */ +goog.ui.DimensionPicker.prototype.maxRows = 20; + + +/** + * Palette dimensions (columns x rows). + * @type {goog.math.Size} + * @private + */ +goog.ui.DimensionPicker.prototype.size_; + + +/** + * Currently highlighted row count. + * @type {number} + * @private + */ +goog.ui.DimensionPicker.prototype.highlightedRows_ = 1; + + +/** + * Currently highlighted column count. + * @type {number} + * @private + */ +goog.ui.DimensionPicker.prototype.highlightedColumns_ = 1; + + +/** @override */ +goog.ui.DimensionPicker.prototype.enterDocument = function() { + goog.ui.DimensionPicker.superClass_.enterDocument.call(this); + + var MouseEventType = goog.ui.ComponentUtil.getMouseEventType(this); + + var handler = this.getHandler(); + handler + .listen( + this.getRenderer().getMouseMoveElement(this), + MouseEventType.MOUSEMOVE, this.handleMouseMove) + .listen( + this.getDomHelper().getWindow(), goog.events.EventType.RESIZE, + this.handleWindowResize); + + var parent = this.getParent(); + if (parent) { + handler.listen(parent, goog.ui.Component.EventType.SHOW, this.handleShow_); + } +}; + + +/** @override */ +goog.ui.DimensionPicker.prototype.exitDocument = function() { + goog.ui.DimensionPicker.superClass_.exitDocument.call(this); + + var MouseEventType = goog.ui.ComponentUtil.getMouseEventType(this); + + var handler = this.getHandler(); + handler + .unlisten( + this.getRenderer().getMouseMoveElement(this), + MouseEventType.MOUSEMOVE, this.handleMouseMove) + .unlisten( + this.getDomHelper().getWindow(), goog.events.EventType.RESIZE, + this.handleWindowResize); + + var parent = this.getParent(); + if (parent) { + handler.unlisten( + parent, goog.ui.Component.EventType.SHOW, this.handleShow_); + } +}; + + +/** + * Resets the highlighted size when the picker is shown. + * @private + */ +goog.ui.DimensionPicker.prototype.handleShow_ = function() { + if (this.isVisible()) { + this.setValue(1, 1); + } +}; + + +/** @override */ +goog.ui.DimensionPicker.prototype.disposeInternal = function() { + goog.ui.DimensionPicker.superClass_.disposeInternal.call(this); + delete this.size_; +}; + + +// Palette event handling. + + +/** + * Handles mousemove events. Determines which palette size was moused over and + * highlights it. + * @param {goog.events.BrowserEvent} e Mouse event to handle. + * @protected + */ +goog.ui.DimensionPicker.prototype.handleMouseMove = function(e) { + var highlightedSizeX = this.getRenderer().getGridOffsetX( + this, this.isRightToLeft() ? + /** @type {!HTMLElement} */ (e.target).offsetWidth - e.offsetX : + e.offsetX); + var highlightedSizeY = this.getRenderer().getGridOffsetY(this, e.offsetY); + + this.setValue(highlightedSizeX, highlightedSizeY); +}; + + +/** + * Override `handleMouseDown` for pointer events. + * @override + */ +goog.ui.DimensionPicker.prototype.handleMouseDown = function(e) { + // For touch events, check for intersection with the grid element to prevent + // taps on the invisible mouse catcher element from performing an action. + if (goog.ui.DimensionPicker.isTouchEvent_(e) && !this.isEventOnGrid_(e)) { + return; + } + + goog.ui.DimensionPicker.base(this, 'handleMouseDown', e); + + // For touch events, delegate to `handleMouseMove` to update the highlight + // state immediately. Not needed for mouse since we assume hover mousemove + // events have already taken care of this. + if (goog.ui.DimensionPicker.isTouchEvent_(e)) { + this.handleMouseMove(/** @type {?goog.events.BrowserEvent} */ (e)); + } +}; + + +/** + * Override `handleMouseUp` for pointer events. + * @override + */ +goog.ui.DimensionPicker.prototype.handleMouseUp = function(e) { + // For touch events, check for intersection with the grid element to prevent + // taps on the invisible mouse catcher element from performing an action. + if (goog.ui.DimensionPicker.isTouchEvent_(e) && !this.isEventOnGrid_(e)) { + return; + } + + goog.ui.DimensionPicker.base(this, 'handleMouseUp', e); +}; + + +/** + * Handles window resize events. Ensures no scrollbars are introduced by the + * renderer's mouse catcher. + * @param {goog.events.Event} e Resize event to handle. + * @protected + */ +goog.ui.DimensionPicker.prototype.handleWindowResize = function(e) { + this.getRenderer().positionMouseCatcher(this); +}; + + +/** + * Handle key events if supported, so the user can use the keyboard to + * manipulate the highlighted rows and columns. + * @param {goog.events.KeyEvent} e The key event object. + * @return {boolean} Whether the key event was handled. + * @override + */ +goog.ui.DimensionPicker.prototype.handleKeyEvent = function(e) { + var rows = this.highlightedRows_; + var columns = this.highlightedColumns_; + switch (e.keyCode) { + case goog.events.KeyCodes.DOWN: + rows++; + break; + case goog.events.KeyCodes.UP: + rows--; + break; + case goog.events.KeyCodes.LEFT: + if (this.isRightToLeft()) { + columns++; + } else { + if (columns == 1) { + // Delegate to parent. + return false; + } else { + columns--; + } + } + break; + case goog.events.KeyCodes.RIGHT: + if (this.isRightToLeft()) { + if (columns == 1) { + // Delegate to parent. + return false; + } else { + columns--; + } + } else { + columns++; + } + break; + default: + return goog.ui.DimensionPicker.superClass_.handleKeyEvent.call(this, e); + } + this.setValue(columns, rows); + return true; +}; + + +// Palette management. + + +/** + * @return {goog.math.Size} Current table size shown (columns x rows). + */ +goog.ui.DimensionPicker.prototype.getSize = function() { + return this.size_; +}; + + +/** + * @return {!goog.math.Size} size The currently highlighted dimensions. + */ +goog.ui.DimensionPicker.prototype.getValue = function() { + return new goog.math.Size(this.highlightedColumns_, this.highlightedRows_); +}; + + +/** + * Sets the currently highlighted dimensions. If the dimensions are not valid + * (not between 1 and the maximum number of columns/rows to show), they will + * be changed to the closest valid value. + * @param {(number|!goog.math.Size)} columns The number of columns to highlight, + * or a goog.math.Size object containing both. + * @param {number=} opt_rows The number of rows to highlight. Can be + * omitted when columns is a good.math.Size object. + */ +goog.ui.DimensionPicker.prototype.setValue = function(columns, opt_rows) { + if (!goog.isDef(opt_rows)) { + columns = /** @type {!goog.math.Size} */ (columns); + opt_rows = columns.height; + columns = columns.width; + } else { + columns = /** @type {number} */ (columns); + } + + // Ensure that the row and column values are within the minimum value (1) and + // maxmimum values. + columns = Math.max(1, columns); + opt_rows = Math.max(1, opt_rows); + columns = Math.min(this.maxColumns, columns); + opt_rows = Math.min(this.maxRows, opt_rows); + + if (this.highlightedColumns_ != columns || + this.highlightedRows_ != opt_rows) { + var renderer = this.getRenderer(); + // Show one more row/column than highlighted so the user understands the + // palette can grow. + this.size_.width = + Math.max(Math.min(columns + 1, this.maxColumns), this.minColumns); + this.size_.height = + Math.max(Math.min(opt_rows + 1, this.maxRows), this.minRows); + renderer.updateSize(this, this.getElement()); + + this.highlightedColumns_ = columns; + this.highlightedRows_ = opt_rows; + renderer.setHighlightedSize(this, columns, opt_rows); + } +}; + + +/** + * Returns whether the given event intersects the grid element. + * @param {?goog.events.Event} e Mouse event to handle. + * @return {boolean} + * @private + */ +goog.ui.DimensionPicker.prototype.isEventOnGrid_ = function(e) { + var gridEl = this.getRenderer().getMouseMoveElement(this); + var gridBounds = gridEl.getBoundingClientRect(); + return e.clientX >= gridBounds.left && e.clientX <= gridBounds.right && + e.clientY >= gridBounds.top && e.clientY <= gridBounds.bottom; +}; + + +/** + * @param {?goog.events.Event} e Mouse or pointer event to handle. + * @return {boolean} + * @private + */ +goog.ui.DimensionPicker.isTouchEvent_ = function(e) { + return e.pointerType && + e.pointerType != goog.events.BrowserEvent.PointerType.MOUSE; +}; + + +/** + * Register this control so it can be created from markup + */ +goog.ui.registry.setDecoratorByClassName( + goog.ui.DimensionPickerRenderer.CSS_CLASS, + function() { return new goog.ui.DimensionPicker(); }); diff --git a/closure-library/closure/goog/ui/dimensionpickerrenderer.js b/closure-library/closure/goog/ui/dimensionpickerrenderer.js new file mode 100644 index 0000000000..6494176e7e --- /dev/null +++ b/closure-library/closure/goog/ui/dimensionpickerrenderer.js @@ -0,0 +1,422 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview The default renderer for a goog.dom.DimensionPicker. A + * dimension picker allows the user to visually select a row and column count. + * It looks like a palette but in order to minimize DOM load it is rendered. + * using CSS background tiling instead of as a grid of nodes. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.ui.DimensionPickerRenderer'); + +goog.require('goog.a11y.aria.Announcer'); +goog.require('goog.a11y.aria.LivePriority'); +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.i18n.bidi'); +goog.require('goog.style'); +goog.require('goog.ui.ControlRenderer'); +goog.require('goog.userAgent'); + +goog.forwardDeclare('goog.ui.DimensionPicker'); + + + +/** + * Default renderer for {@link goog.ui.DimensionPicker}s. Renders the + * palette as two divs, one with the un-highlighted background, and one with the + * highlighted background. + * + * @constructor + * @extends {goog.ui.ControlRenderer} + */ +goog.ui.DimensionPickerRenderer = function() { + goog.ui.ControlRenderer.call(this); + + /** @private {goog.a11y.aria.Announcer} */ + this.announcer_ = new goog.a11y.aria.Announcer(); +}; +goog.inherits(goog.ui.DimensionPickerRenderer, goog.ui.ControlRenderer); +goog.addSingletonGetter(goog.ui.DimensionPickerRenderer); + + +/** + * Default CSS class to be applied to the root element of components rendered + * by this renderer. + * @type {string} + */ +goog.ui.DimensionPickerRenderer.CSS_CLASS = + goog.getCssName('goog-dimension-picker'); + + +/** + * Return the underlying div for the given outer element. + * @param {Element} element The root element. + * @return {Element} The underlying div. + * @private + */ +goog.ui.DimensionPickerRenderer.prototype.getUnderlyingDiv_ = function( + element) { + return /** @type {Element} */ (element.firstChild.childNodes[1]); +}; + + +/** + * Return the highlight div for the given outer element. + * @param {Element} element The root element. + * @return {Element} The highlight div. + * @private + */ +goog.ui.DimensionPickerRenderer.prototype.getHighlightDiv_ = function(element) { + return /** @type {Element} */ (element.firstChild.lastChild); +}; + + +/** + * Return the status message div for the given outer element. + * @param {Element} element The root element. + * @return {Element} The status message div. + * @private + */ +goog.ui.DimensionPickerRenderer.prototype.getStatusDiv_ = function(element) { + return /** @type {Element} */ (element.lastChild); +}; + + +/** + * Return the invisible mouse catching div for the given outer element. + * @param {Element} element The root element. + * @return {Element} The invisible mouse catching div. + * @private + */ +goog.ui.DimensionPickerRenderer.prototype.getMouseCatcher_ = function(element) { + return /** @type {Element} */ (element.firstChild.firstChild); +}; + + +/** + * Overrides {@link goog.ui.ControlRenderer#canDecorate} to allow decorating + * empty DIVs only. + * @param {Element} element The element to check. + * @return {boolean} Whether if the element is an empty div. + * @override + */ +goog.ui.DimensionPickerRenderer.prototype.canDecorate = function(element) { + return element.tagName == goog.dom.TagName.DIV && !element.firstChild; +}; + + +/** + * Overrides {@link goog.ui.ControlRenderer#decorate} to decorate empty DIVs. + * @param {goog.ui.Control} control goog.ui.DimensionPicker to decorate. + * @param {Element} element The element to decorate. + * @return {Element} The decorated element. + * @override + */ +goog.ui.DimensionPickerRenderer.prototype.decorate = function( + control, element) { + var palette = /** @type {goog.ui.DimensionPicker} */ (control); + goog.ui.DimensionPickerRenderer.superClass_.decorate.call( + this, palette, element); + + this.addElementContents_(palette, element); + this.updateSize(palette, element); + + return element; +}; + + +/** + * Scales various elements in order to update the palette's size. + * @param {goog.ui.DimensionPicker} palette The palette object. + * @param {Element} element The element to set the style of. + */ +goog.ui.DimensionPickerRenderer.prototype.updateSize = function( + palette, element) { + var size = palette.getSize(); + + element.style.width = size.width + 'em'; + + var underlyingDiv = this.getUnderlyingDiv_(element); + underlyingDiv.style.width = size.width + 'em'; + underlyingDiv.style.height = size.height + 'em'; + + if (palette.isRightToLeft()) { + this.adjustParentDirection_(palette, element); + } +}; + + +/** + * Adds the appropriate content elements to the given outer DIV. + * @param {goog.ui.DimensionPicker} palette The palette object. + * @param {Element} element The element to decorate. + * @private + */ +goog.ui.DimensionPickerRenderer.prototype.addElementContents_ = function( + palette, element) { + // First we create a single div containing three stacked divs. The bottom div + // catches mouse events. We can't use document level mouse move detection as + // we could lose events to iframes. This is especially important in Firefox 2 + // in which TrogEdit creates iframes. The middle div uses a css tiled + // background image to represent deselected tiles. The top div uses a + // different css tiled background image to represent selected tiles. + var mouseCatcherDiv = palette.getDomHelper().createDom( + goog.dom.TagName.DIV, + goog.getCssName(this.getCssClass(), 'mousecatcher')); + var unhighlightedDiv = + palette.getDomHelper().createDom(goog.dom.TagName.DIV, { + 'class': goog.getCssName(this.getCssClass(), 'unhighlighted'), + 'style': 'width:100%;height:100%' + }); + var highlightedDiv = palette.getDomHelper().createDom( + goog.dom.TagName.DIV, goog.getCssName(this.getCssClass(), 'highlighted')); + element.appendChild( + palette.getDomHelper().createDom( + goog.dom.TagName.DIV, { + 'style': 'width:100%;height:100%;touch-action:none;' + }, + mouseCatcherDiv, unhighlightedDiv, highlightedDiv)); + + // Lastly we add a div to store the text version of the current state. + element.appendChild( + palette.getDomHelper().createDom( + goog.dom.TagName.DIV, goog.getCssName(this.getCssClass(), 'status'))); +}; + + +/** + * Creates a div and adds the appropriate contents to it. + * @param {goog.ui.Control} control Picker to render. + * @return {!Element} Root element for the palette. + * @override + */ +goog.ui.DimensionPickerRenderer.prototype.createDom = function(control) { + var palette = /** @type {goog.ui.DimensionPicker} */ (control); + var classNames = this.getClassNames(palette); + // Hide the element from screen readers so they don't announce "1 of 1" for + // the perceived number of items in the palette. + var element = palette.getDomHelper().createDom( + goog.dom.TagName.DIV, + {'class': classNames ? classNames.join(' ') : '', 'aria-hidden': 'true'}); + this.addElementContents_(palette, element); + this.updateSize(palette, element); + return element; +}; + + +/** + * Initializes the control's DOM when the control enters the document. Called + * from {@link goog.ui.Control#enterDocument}. + * @param {goog.ui.Control} control Palette whose DOM is to be + * initialized as it enters the document. + * @override + */ +goog.ui.DimensionPickerRenderer.prototype.initializeDom = function(control) { + var palette = /** @type {goog.ui.DimensionPicker} */ (control); + goog.ui.DimensionPickerRenderer.superClass_.initializeDom.call(this, palette); + + // Make the displayed highlighted size match the dimension picker's value. + var highlightedSize = palette.getValue(); + this.setHighlightedSize( + palette, highlightedSize.width, highlightedSize.height); + + this.positionMouseCatcher(palette); +}; + + +/** + * Get the element to listen for mouse move events on. + * @param {goog.ui.DimensionPicker} palette The palette to listen on. + * @return {Element} The element to listen for mouse move events on. + */ +goog.ui.DimensionPickerRenderer.prototype.getMouseMoveElement = function( + palette) { + return /** @type {Element} */ (palette.getElement().firstChild); +}; + + +/** + * Returns the x offset in to the grid for the given mouse x position. + * @param {goog.ui.DimensionPicker} palette The table size palette. + * @param {number} x The mouse event x position. + * @return {number} The x offset in to the grid. + */ +goog.ui.DimensionPickerRenderer.prototype.getGridOffsetX = function( + palette, x) { + // TODO(robbyw): Don't rely on magic 18 - measure each palette's em size. + return Math.min(palette.maxColumns, Math.ceil(x / 18)); +}; + + +/** + * Returns the y offset in to the grid for the given mouse y position. + * @param {goog.ui.DimensionPicker} palette The table size palette. + * @param {number} y The mouse event y position. + * @return {number} The y offset in to the grid. + */ +goog.ui.DimensionPickerRenderer.prototype.getGridOffsetY = function( + palette, y) { + return Math.min(palette.maxRows, Math.ceil(y / 18)); +}; + + +/** + * Sets the highlighted size. Does nothing if the palette hasn't been rendered. + * @param {goog.ui.DimensionPicker} palette The table size palette. + * @param {number} columns The number of columns to highlight. + * @param {number} rows The number of rows to highlight. + */ +goog.ui.DimensionPickerRenderer.prototype.setHighlightedSize = function( + palette, columns, rows) { + var element = palette.getElement(); + // Can't update anything if DimensionPicker hasn't been rendered. + if (!element) { + return; + } + + // Style the highlight div. + var style = this.getHighlightDiv_(element).style; + style.width = columns + 'em'; + style.height = rows + 'em'; + + // Explicitly set style.right so the element grows to the left when increase + // in width. + if (palette.isRightToLeft()) { + style.right = '0'; + } + + /** + * @desc The dimension of the columns and rows currently selected in the + * dimension picker, as text that can be spoken by a screen reader. + */ + var MSG_DIMENSION_PICKER_HIGHLIGHTED_DIMENSIONS = goog.getMsg( + '{$numCols} by {$numRows}', + {'numCols': String(columns), 'numRows': String(rows)}); + this.announcer_.say( + MSG_DIMENSION_PICKER_HIGHLIGHTED_DIMENSIONS, + goog.a11y.aria.LivePriority.ASSERTIVE); + + // Update the size text. + goog.dom.setTextContent( + this.getStatusDiv_(element), + goog.i18n.bidi.enforceLtrInText(columns + ' x ' + rows)); +}; + + +/** + * Position the mouse catcher such that it receives mouse events past the + * selectedsize up to the maximum size. Takes care to not introduce scrollbars. + * Should be called on enter document and when the window changes size. + * @param {goog.ui.DimensionPicker} palette The table size palette. + */ +goog.ui.DimensionPickerRenderer.prototype.positionMouseCatcher = function( + palette) { + var mouseCatcher = this.getMouseCatcher_(palette.getElement()); + var doc = goog.dom.getOwnerDocument(mouseCatcher); + var body = doc.body; + + var position = goog.style.getRelativePosition(mouseCatcher, body); + + // Hide the mouse catcher so it doesn't affect the body's scroll size. + mouseCatcher.style.display = 'none'; + + // Compute the maximum size the catcher can be without introducing scrolling. + var xAvailableEm = (palette.isRightToLeft() && position.x > 0) ? + Math.floor(position.x / 18) : + Math.floor((body.scrollWidth - position.x) / 18); + + // Computing available height is more complicated - we need to check the + // window's inner height. + var height; + if (goog.userAgent.IE) { + // Offset 20px to make up for scrollbar size. + height = goog.style.getClientViewportElement(body).scrollHeight - 20; + } else { + var win = goog.dom.getWindow(doc); + // Offset 20px to make up for scrollbar size. + height = Math.max(win.innerHeight, body.scrollHeight) - 20; + } + var yAvailableEm = Math.floor((height - position.y) / 18); + + // Resize and display the mouse catcher. + mouseCatcher.style.width = Math.min(palette.maxColumns, xAvailableEm) + 'em'; + mouseCatcher.style.height = Math.min(palette.maxRows, yAvailableEm) + 'em'; + mouseCatcher.style.display = ''; + + // Explicitly set style.right so the mouse catcher is positioned on the left + // side instead of right. + if (palette.isRightToLeft()) { + mouseCatcher.style.right = '0'; + } +}; + + +/** + * Returns the CSS class to be applied to the root element of components + * rendered using this renderer. + * @return {string} Renderer-specific CSS class. + * @override + */ +goog.ui.DimensionPickerRenderer.prototype.getCssClass = function() { + return goog.ui.DimensionPickerRenderer.CSS_CLASS; +}; + + +/** + * This function adjusts the positioning from 'left' and 'top' to 'right' and + * 'top' as appropriate for RTL control. This is so when the dimensionpicker + * grow in width, the containing element grow to the left instead of right. + * This won't be necessary if goog.ui.SubMenu rendering code would position RTL + * control with 'right' and 'top'. + * @private + * + * @param {goog.ui.DimensionPicker} palette The palette object. + * @param {Element} element The palette's element. + */ +goog.ui.DimensionPickerRenderer.prototype.adjustParentDirection_ = function( + palette, element) { + var parent = palette.getParent(); + if (parent) { + var parentElement = parent.getElement(); + + // Anchors the containing element to the right so it grows to the left + // when it increase in width. + var right = goog.style.getStyle(parentElement, 'right'); + if (right == '') { + var parentPos = goog.style.getPosition(parentElement); + var parentSize = goog.style.getSize(parentElement); + if (parentSize.width != 0 && parentPos.x != 0) { + var visibleRect = + goog.style.getBounds(goog.style.getClientViewportElement()); + var visibleWidth = visibleRect.width; + right = visibleWidth - parentPos.x - parentSize.width; + goog.style.setStyle(parentElement, 'right', right + 'px'); + } + } + + // When a table is inserted, the containing elemet's position is + // recalculated the next time it shows, set left back to '' to prevent + // extra white space on the left. + var left = goog.style.getStyle(parentElement, 'left'); + if (left != '') { + goog.style.setStyle(parentElement, 'left', ''); + } + } else { + goog.style.setStyle(element, 'right', '0px'); + } +}; diff --git a/closure-library/closure/goog/ui/dragdropdetector.js b/closure-library/closure/goog/ui/dragdropdetector.js new file mode 100644 index 0000000000..f91456b442 --- /dev/null +++ b/closure-library/closure/goog/ui/dragdropdetector.js @@ -0,0 +1,650 @@ +// Copyright 2007 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 Detects images dragged and dropped on to the window. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.ui.DragDropDetector'); +goog.provide('goog.ui.DragDropDetector.EventType'); +goog.provide('goog.ui.DragDropDetector.ImageDropEvent'); +goog.provide('goog.ui.DragDropDetector.LinkDropEvent'); + +goog.require('goog.dom'); +goog.require('goog.dom.InputType'); +goog.require('goog.dom.TagName'); +goog.require('goog.events.Event'); +goog.require('goog.events.EventHandler'); +goog.require('goog.events.EventTarget'); +goog.require('goog.events.EventType'); +goog.require('goog.math.Coordinate'); +goog.require('goog.string'); +goog.require('goog.style'); +goog.require('goog.userAgent'); + + + +/** + * Creates a new drag and drop detector. + * @param {string=} opt_filePath The URL of the page to use for the detector. + * It should contain the same contents as dragdropdetector_target.html in + * the demos directory. + * @constructor + * @extends {goog.events.EventTarget} + * @final + */ +goog.ui.DragDropDetector = function(opt_filePath) { + goog.ui.DragDropDetector.base(this, 'constructor'); + + var iframe = goog.dom.createDom(goog.dom.TagName.IFRAME, {'frameborder': 0}); + // In Firefox, we do all drop detection with an IFRAME. In IE, we only use + // the IFRAME to capture copied, non-linked images. (When we don't need it, + // we put a text INPUT before it and push it off screen.) + iframe.className = goog.userAgent.IE ? + goog.getCssName( + goog.ui.DragDropDetector.BASE_CSS_NAME_, 'ie-editable-iframe') : + goog.getCssName( + goog.ui.DragDropDetector.BASE_CSS_NAME_, 'w3c-editable-iframe'); + iframe.src = opt_filePath || goog.ui.DragDropDetector.DEFAULT_FILE_PATH_; + + this.element_ = /** @type {!HTMLIFrameElement} */ (iframe); + + this.handler_ = new goog.events.EventHandler(this); + this.handler_.listen(iframe, goog.events.EventType.LOAD, this.initIframe_); + + if (goog.userAgent.IE) { + // In IE, we have to bounce between an INPUT for catching links and an + // IFRAME for catching images. + this.textInput_ = goog.dom.createDom(goog.dom.TagName.INPUT, { + 'type': goog.dom.InputType.TEXT, + 'className': + goog.getCssName(goog.ui.DragDropDetector.BASE_CSS_NAME_, 'ie-input') + }); + + this.root_ = goog.dom.createDom( + goog.dom.TagName.DIV, + goog.getCssName(goog.ui.DragDropDetector.BASE_CSS_NAME_, 'ie-div'), + this.textInput_, iframe); + } else { + this.root_ = iframe; + } + + document.body.appendChild(this.root_); +}; +goog.inherits(goog.ui.DragDropDetector, goog.events.EventTarget); + + +/** + * Drag and drop event types. + * @enum {string} + */ +goog.ui.DragDropDetector.EventType = { + IMAGE_DROPPED: 'onimagedrop', + LINK_DROPPED: 'onlinkdrop' +}; + + +/** + * Browser specific drop event type. + * @type {string} + * @private + */ +goog.ui.DragDropDetector.DROP_EVENT_TYPE_ = + goog.userAgent.IE ? goog.events.EventType.DROP : 'dragdrop'; + + +/** + * Initial value for clientX and clientY indicating that the location has + * never been updated. + */ +goog.ui.DragDropDetector.INIT_POSITION = -10000; + + +/** + * Prefix for all CSS names. + * @type {string} + * @private + */ +goog.ui.DragDropDetector.BASE_CSS_NAME_ = goog.getCssName('goog-dragdrop'); + + +/** + * @desc Message shown to users to inform them that they can't drag and drop + * local files. + */ +goog.ui.DragDropDetector.MSG_DRAG_DROP_LOCAL_FILE_ERROR = goog.getMsg( + 'It is not possible to drag ' + + 'and drop image files at this time.\nPlease drag an image from your web ' + + 'browser.'); + + +/** + * @desc Message shown to users trying to drag and drop protected images from + * Flickr, etc. + */ +goog.ui.DragDropDetector.MSG_DRAG_DROP_PROTECTED_FILE_ERROR = goog.getMsg( + 'The image you are ' + + 'trying to drag has been blocked by the hosting site.'); + + +/** + * A map of special case information for URLs that cannot be dropped. Each + * entry is of the form: + * regex: url regex + * message: user visible message about this special case + * @type {Array<{regex: RegExp, message: string}>} + * @private + */ +goog.ui.DragDropDetector.SPECIAL_CASE_URLS_ = [ + { + regex: /^file:\/\/\//, + message: goog.ui.DragDropDetector.MSG_DRAG_DROP_LOCAL_FILE_ERROR + }, + { + regex: /flickr(.*)spaceball.gif$/, + message: goog.ui.DragDropDetector.MSG_DRAG_DROP_PROTECTED_FILE_ERROR + } +]; + + +/** + * Regex that matches anything that looks kind of like a URL. It matches + * nonspacechars://nonspacechars + * @type {RegExp} + * @private + */ +goog.ui.DragDropDetector.URL_LIKE_REGEX_ = /^\S+:\/\/\S*$/; + + +/** + * Path to the dragdrop.html file. + * @type {string} + * @private + */ +goog.ui.DragDropDetector.DEFAULT_FILE_PATH_ = 'dragdropdetector_target.html'; + + +/** + * Our event handler object. + * @type {goog.events.EventHandler} + * @private + */ +goog.ui.DragDropDetector.prototype.handler_; + + +/** + * The root element (the IFRAME on most browsers, the DIV on IE). + * @type {Element} + * @private + */ +goog.ui.DragDropDetector.prototype.root_; + + +/** + * The text INPUT element used to detect link drops on IE. null on Firefox. + * @type {Element} + * @private + */ +goog.ui.DragDropDetector.prototype.textInput_; + + +/** + * The iframe element. + * @type {HTMLIFrameElement} + * @private + */ +goog.ui.DragDropDetector.prototype.element_; + + +/** + * The iframe's window, null if the iframe hasn't loaded yet. + * @type {Window} + * @private + */ +goog.ui.DragDropDetector.prototype.window_ = null; + + +/** + * The iframe's document, null if the iframe hasn't loaded yet. + * @type {Document} + * @private + */ +goog.ui.DragDropDetector.prototype.document_ = null; + + +/** + * The iframe's body, null if the iframe hasn't loaded yet. + * @type {HTMLBodyElement} + * @private + */ +goog.ui.DragDropDetector.prototype.body_ = null; + + +/** + * Whether we are in "screen cover" mode in which the iframe or div is + * covering the entire screen. + * @type {boolean} + * @private + */ +goog.ui.DragDropDetector.prototype.isCoveringScreen_ = false; + + +/** + * The last position of the mouse while dragging. + * @type {goog.math.Coordinate} + * @private + */ +goog.ui.DragDropDetector.prototype.mousePosition_ = null; + + +/** + * Initialize the iframe after it has loaded. + * @private + */ +goog.ui.DragDropDetector.prototype.initIframe_ = function() { + // Set up a holder for position data. + this.mousePosition_ = new goog.math.Coordinate( + goog.ui.DragDropDetector.INIT_POSITION, + goog.ui.DragDropDetector.INIT_POSITION); + + // Set up pointers to the important parts of the IFrame. + this.window_ = this.element_.contentWindow; + this.document_ = this.window_.document; + this.body_ = this.document_.body; + + if (goog.userAgent.GECKO) { + this.document_.designMode = 'on'; + } else if (!goog.userAgent.IE) { + // Bug 1667110 + // In IE, we only set the IFrame body as content-editable when we bring it + // into view at the top of the page. Otherwise it may take focus when the + // page is loaded, scrolling the user far offscreen. + // Note that this isn't easily unit-testable, since it depends on a + // browser-specific behavior with content-editable areas. + this.body_.contentEditable = true; + } + + this.handler_.listen( + document.body, goog.events.EventType.DRAGENTER, this.coverScreen_); + + if (goog.userAgent.IE) { + // IE only events. + // Set up events on the IFrame. + this.handler_ + .listen( + this.body_, + [goog.events.EventType.DRAGENTER, goog.events.EventType.DRAGOVER], + goog.ui.DragDropDetector.enforceCopyEffect_) + .listen(this.body_, goog.events.EventType.MOUSEOUT, this.switchToInput_) + .listen( + this.body_, goog.events.EventType.DRAGLEAVE, this.uncoverScreen_) + .listen( + this.body_, goog.ui.DragDropDetector.DROP_EVENT_TYPE_, + function(e) { + this.trackMouse_(e); + + // The drop event occurs before the content is added to the + // iframe. We setTimeout so that handleNodeInserted_ is called + // after the content is in the document. + goog.global.setTimeout( + goog.bind(this.handleNodeInserted_, this, e), 0); + return true; + }) + . + + // Set up events on the DIV. + listen( + this.root_, + [goog.events.EventType.DRAGENTER, goog.events.EventType.DRAGOVER], + this.handleNewDrag_) + .listen( + this.root_, + [goog.events.EventType.MOUSEMOVE, goog.events.EventType.KEYPRESS], + this.uncoverScreen_) + . + + // Set up events on the text INPUT. + listen( + this.textInput_, goog.events.EventType.DRAGOVER, + goog.events.Event.preventDefault) + .listen( + this.textInput_, goog.ui.DragDropDetector.DROP_EVENT_TYPE_, + this.handleInputDrop_); + } else { + // W3C events. + this.handler_ + .listen( + this.body_, goog.ui.DragDropDetector.DROP_EVENT_TYPE_, + function(e) { + this.trackMouse_(e); + this.uncoverScreen_(); + }) + .listen( + this.body_, + [goog.events.EventType.MOUSEMOVE, goog.events.EventType.KEYPRESS], + this.uncoverScreen_) + // Detect content insertion. + .listen(this.document_, 'DOMNodeInserted', this.handleNodeInserted_); + } +}; + + +/** + * Enforce that anything dragged over the IFRAME is copied in to it, rather + * than making it navigate to a different URL. + * @param {goog.events.BrowserEvent} e The event to enforce copying on. + * @private + */ +goog.ui.DragDropDetector.enforceCopyEffect_ = function(e) { + var event = e.getBrowserEvent(); + // This function is only called on IE. + if (event.dataTransfer.dropEffect.toLowerCase() != 'copy') { + event.dataTransfer.dropEffect = 'copy'; + } +}; + + +/** + * Cover the screen with the iframe. + * @param {goog.events.BrowserEvent} e The event that caused this function call. + * @private + */ +goog.ui.DragDropDetector.prototype.coverScreen_ = function(e) { + // Don't do anything if the drop effect is 'none' and we are in IE. + // It is set to 'none' in cases like dragging text inside a text area. + if (goog.userAgent.IE && + e.getBrowserEvent().dataTransfer.dropEffect == 'none') { + return; + } + + if (!this.isCoveringScreen_) { + this.isCoveringScreen_ = true; + if (goog.userAgent.IE) { + goog.style.setStyle(this.root_, 'top', '0'); + this.body_.contentEditable = true; + this.switchToInput_(e); + } else { + goog.style.setStyle(this.root_, 'height', '5000px'); + } + } +}; + + +/** + * Uncover the screen. + * @private + */ +goog.ui.DragDropDetector.prototype.uncoverScreen_ = function() { + if (this.isCoveringScreen_) { + this.isCoveringScreen_ = false; + if (goog.userAgent.IE) { + this.body_.contentEditable = false; + goog.style.setStyle(this.root_, 'top', '-5000px'); + } else { + goog.style.setStyle(this.root_, 'height', '10px'); + } + } +}; + + +/** + * Re-insert the INPUT into the DIV. Does nothing when the DIV is off screen. + * @param {goog.events.BrowserEvent} e The event that caused this function call. + * @private + */ +goog.ui.DragDropDetector.prototype.switchToInput_ = function(e) { + // This is only called on IE. + if (this.isCoveringScreen_) { + goog.style.setElementShown(this.textInput_, true); + } +}; + + +/** + * Remove the text INPUT so the IFRAME is showing. Does nothing when the DIV is + * off screen. + * @param {goog.events.BrowserEvent} e The event that caused this function call. + * @private + */ +goog.ui.DragDropDetector.prototype.switchToIframe_ = function(e) { + // This is only called on IE. + if (this.isCoveringScreen_) { + goog.style.setElementShown(this.textInput_, false); + } +}; + + +/** + * Handle a new drag event. + * @param {goog.events.BrowserEvent} e The event object. + * @return {boolean|undefined} Returns false in IE to cancel the event. + * @private + */ +goog.ui.DragDropDetector.prototype.handleNewDrag_ = function(e) { + var event = e.getBrowserEvent(); + + // This is only called on IE. + if (event.dataTransfer.dropEffect == 'link') { + this.switchToInput_(e); + e.preventDefault(); + return false; + } + + // Things that aren't links can be placed in the contentEditable iframe. + this.switchToIframe_(e); + + // No need to return true since for events return true is the same as no + // return. +}; + + +/** + * Handle mouse tracking. + * @param {goog.events.BrowserEvent} e The event object. + * @private + */ +goog.ui.DragDropDetector.prototype.trackMouse_ = function(e) { + this.mousePosition_.x = e.clientX; + this.mousePosition_.y = e.clientY; + + // Check if the event is coming from within the iframe. + if (goog.dom.getOwnerDocument(/** @type {Node} */ (e.target)) != document) { + var iframePosition = goog.style.getClientPosition(this.element_); + this.mousePosition_.x += iframePosition.x; + this.mousePosition_.y += iframePosition.y; + } +}; + + +/** + * Handle a drop on the IE text INPUT. + * @param {goog.events.BrowserEvent} e The event object. + * @private + */ +goog.ui.DragDropDetector.prototype.handleInputDrop_ = function(e) { + this.dispatchEvent( + new goog.ui.DragDropDetector.LinkDropEvent( + e.getBrowserEvent().dataTransfer.getData('Text'))); + this.uncoverScreen_(); + e.preventDefault(); +}; + + +/** + * Clear the contents of the iframe. + * @private + */ +goog.ui.DragDropDetector.prototype.clearContents_ = function() { + if (goog.userAgent.WEBKIT) { + // Since this is called on a mutation event for the nodes we are going to + // clear, calling this right away crashes some versions of WebKit. Wait + // until the events are finished. + goog.global.setTimeout(goog.bind(function() { + goog.dom.setTextContent(this, ''); + }, this.body_), 0); + } else { + this.document_.execCommand('selectAll', false, null); + this.document_.execCommand('delete', false, null); + this.document_.execCommand('selectAll', false, null); + } +}; + + +/** + * Event handler called when the content of the iframe changes. + * @param {goog.events.BrowserEvent} e The event that caused this function call. + * @private + */ +goog.ui.DragDropDetector.prototype.handleNodeInserted_ = function(e) { + var uri; + + if (this.body_.innerHTML.indexOf('<') == -1) { + // If the document contains no tags (i.e. is just text), try it out. + uri = goog.string.trim(goog.dom.getTextContent(this.body_)); + + // See if it looks kind of like a url. + if (!uri.match(goog.ui.DragDropDetector.URL_LIKE_REGEX_)) { + uri = null; + } + } + + if (!uri) { + var imgs = goog.dom.getElementsByTagName(goog.dom.TagName.IMG, this.body_); + if (imgs && imgs.length) { + // TODO(robbyw): Grab all the images, instead of just the first. + var img = imgs[0]; + uri = img.src; + } + } + + if (uri) { + var specialCases = goog.ui.DragDropDetector.SPECIAL_CASE_URLS_; + var len = specialCases.length; + for (var i = 0; i < len; i++) { + var specialCase = specialCases[i]; + if (uri.match(specialCase.regex)) { + alert(specialCase.message); + break; + } + } + + // If no special cases matched, add the image. + if (i == len) { + this.dispatchEvent( + new goog.ui.DragDropDetector.ImageDropEvent( + uri, this.mousePosition_)); + return; + } + } + + var links = goog.dom.getElementsByTagName(goog.dom.TagName.A, this.body_); + if (links) { + for (i = 0, len = links.length; i < len; i++) { + this.dispatchEvent( + new goog.ui.DragDropDetector.LinkDropEvent(links[i].href)); + } + } + + this.clearContents_(); + this.uncoverScreen_(); +}; + + +/** @override */ +goog.ui.DragDropDetector.prototype.disposeInternal = function() { + goog.ui.DragDropDetector.base(this, 'disposeInternal'); + this.handler_.dispose(); + this.handler_ = null; +}; + + + +/** + * Creates a new image drop event object. + * @param {string} url The url of the dropped image. + * @param {goog.math.Coordinate} position The screen position where the drop + * occurred. + * @constructor + * @extends {goog.events.Event} + * @final + */ +goog.ui.DragDropDetector.ImageDropEvent = function(url, position) { + goog.ui.DragDropDetector.ImageDropEvent.base( + this, 'constructor', goog.ui.DragDropDetector.EventType.IMAGE_DROPPED); + + /** + * The url of the image that was dropped. + * @type {string} + * @private + */ + this.url_ = url; + + /** + * The screen position where the drop occurred. + * @type {goog.math.Coordinate} + * @private + */ + this.position_ = position; +}; +goog.inherits(goog.ui.DragDropDetector.ImageDropEvent, goog.events.Event); + + +/** + * @return {string} The url of the image that was dropped. + */ +goog.ui.DragDropDetector.ImageDropEvent.prototype.getUrl = function() { + return this.url_; +}; + + +/** + * @return {goog.math.Coordinate} The screen position where the drop occurred. + * This may be have x and y of goog.ui.DragDropDetector.INIT_POSITION, + * indicating the drop position is unknown. + */ +goog.ui.DragDropDetector.ImageDropEvent.prototype.getPosition = function() { + return this.position_; +}; + + + +/** + * Creates a new link drop event object. + * @param {string} url The url of the dropped link. + * @constructor + * @extends {goog.events.Event} + * @final + */ +goog.ui.DragDropDetector.LinkDropEvent = function(url) { + goog.ui.DragDropDetector.LinkDropEvent.base( + this, 'constructor', goog.ui.DragDropDetector.EventType.LINK_DROPPED); + + /** + * The url of the link that was dropped. + * @type {string} + * @private + */ + this.url_ = url; +}; +goog.inherits(goog.ui.DragDropDetector.LinkDropEvent, goog.events.Event); + + +/** + * @return {string} The url of the link that was dropped. + */ +goog.ui.DragDropDetector.LinkDropEvent.prototype.getUrl = function() { + return this.url_; +}; diff --git a/closure-library/closure/goog/ui/drilldownrow.js b/closure-library/closure/goog/ui/drilldownrow.js new file mode 100644 index 0000000000..fa6e0757a2 --- /dev/null +++ b/closure-library/closure/goog/ui/drilldownrow.js @@ -0,0 +1,511 @@ +// Copyright 2007 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 Tree-like drilldown components for HTML tables. + * + * This component supports expanding and collapsing groups of rows in + * HTML tables. The behavior is like typical Tree widgets, but tables + * need special support to enable the tree behaviors. + * + * Any row or rows in an HTML table can be DrilldownRows. The root + * DrilldownRow nodes are always visible in the table, but the rest show + * or hide as input events expand and collapse their ancestors. + * + * Programming them: Top-level DrilldownRows are made by decorating + * a TR element. Children are made with addChild or addChildAt, and + * are entered into the document by the render() method. + * + * A DrilldownRow can have any number of children. If it has no children + * it can be loaded, not loaded, or with a load in progress. + * Top-level DrilldownRows are always displayed (though setting + * style.display on a containing DOM node could make one be not + * visible to the user). A DrilldownRow can be expanded, or not. A + * DrilldownRow displays if all of its ancestors are expanded. + * + * Set up event handlers and style each row for the application in an + * enterDocument method. + * + * Children normally render into the document lazily, at the first + * moment when all ancestors are expanded. + * + * @see ../demos/drilldownrow.html + */ + +// TODO(user): Build support for dynamically loading DrilldownRows, +// probably using automplete as an example to follow. + +// TODO(user): Make DrilldownRows accessible through the keyboard. + +// The render method is redefined in this class because when addChildAt renders +// the new child it assumes that the child's DOM node will be a child +// of the parent component's DOM node, but all DOM nodes of DrilldownRows +// in the same tree of DrilldownRows are siblings to each other. +// +// Arguments (or lack of arguments) to the render methods in Component +// all determine the place of the new DOM node in the DOM tree, but +// the place of a new DrilldownRow in the DOM needs to be determined by +// its position in the tree of DrilldownRows. + +goog.provide('goog.ui.DrilldownRow'); + +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.classlist'); +goog.require('goog.dom.safe'); +goog.require('goog.html.SafeHtml'); +goog.require('goog.string.Unicode'); +goog.require('goog.ui.Component'); + + + +/** + * Builds a DrilldownRow component, which can overlay a tree + * structure onto sections of an HTML table. + * + * @param {!goog.ui.DrilldownRow.DrilldownRowProperties=} opt_properties + * Optional properties. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper. + * @constructor + * @extends {goog.ui.Component} + * @final + */ +goog.ui.DrilldownRow = function(opt_properties, opt_domHelper) { + goog.ui.Component.call(this, opt_domHelper); + var properties = opt_properties || {}; + + // Initialize instance variables. + + var html; + if (!goog.isDefAndNotNull(properties.html)) { + html = goog.html.SafeHtml.EMPTY; + } else { + goog.asserts.assert(properties.html instanceof goog.html.SafeHtml); + html = properties.html; + } + /** + * String of HTML to initialize the DOM structure for the table row. + * Should have the form 'Row contents here'. + * @type {!goog.html.SafeHtml} + * @private + */ + this.html_ = html; + + /** + * Controls whether this component's children will show when it shows. + * @type {boolean} + * @private + */ + this.expanded_ = + typeof properties.expanded != 'undefined' ? properties.expanded : true; + + /** + * If this component's DOM element is created from a string of + * HTML, this is the function to call when it is entered into the DOM tree. + * @type {Function} args are DrilldownRow and goog.events.EventHandler + * of the DrilldownRow. + * @private + */ + this.decoratorFn_ = properties.decorator || goog.ui.DrilldownRow.decorate; + + /** + * Is the DrilldownRow to be displayed? If it is rendered, this mirrors + * the style.display of the DrilldownRow's row. + * @type {boolean} + * @private + */ + this.displayed_ = true; +}; +goog.inherits(goog.ui.DrilldownRow, goog.ui.Component); + + +/** + * Used to define properties for a new DrilldownRow. Properties can contain: + * loaded: initializes the isLoaded property, defaults to true. + * expanded: DrilldownRow expanded or not, default is true. + * html: Relevant and required for DrilldownRows to be added as + * children. Ignored when decorating an existing table row. + * decorator: Function that accepts one DrilldownRow argument, and + * should customize and style the row. The default is to call + * goog.ui.DrilldownRow.decorator. + * @typedef {{ + * loaded: (boolean|undefined), + * expanded: (boolean|undefined), + * html: (!goog.html.SafeHtml|undefined), + * decorator: (Function|undefined) + * }} + */ +goog.ui.DrilldownRow.DrilldownRowProperties; + + +/** + * Example object with properties of the form accepted by the class + * constructor. These are educational and show the compiler that + * these properties can be set so it doesn't emit warnings. + */ +goog.ui.DrilldownRow.sampleProperties = { + html: goog.html.SafeHtml.create( + goog.dom.TagName.TR, {}, + goog.html.SafeHtml.concat( + goog.html.SafeHtml.create(goog.dom.TagName.TD, {}, 'Sample'), + goog.html.SafeHtml.create(goog.dom.TagName.TD, {}, 'Sample'))), + loaded: true, + decorator: function(selfObj, handler) { + // When the mouse is hovering, add CSS class goog-drilldown-hover. + goog.ui.DrilldownRow.decorate(selfObj); + var row = selfObj.getElement(); + handler.listen(row, 'mouseover', function() { + goog.dom.classlist.add(row, goog.getCssName('goog-drilldown-hover')); + }); + handler.listen(row, 'mouseout', function() { + goog.dom.classlist.remove(row, goog.getCssName('goog-drilldown-hover')); + }); + } +}; + + +// +// Implementations of Component methods. +// + + +/** + * The base class method calls its superclass method and this + * drilldown's 'decorator' method as defined in the constructor. + * @override + */ +goog.ui.DrilldownRow.prototype.enterDocument = function() { + goog.ui.DrilldownRow.superClass_.enterDocument.call(this); + this.decoratorFn_(this, this.getHandler()); +}; + + +/** @override */ +goog.ui.DrilldownRow.prototype.createDom = function() { + this.setElementInternal( + goog.ui.DrilldownRow.createRowNode_(this.html_, this.getDomHelper())); +}; + + +/** + * A top-level DrilldownRow decorates a TR element. + * + * @param {Element} node The element to test for decorability. + * @return {boolean} true iff the node is a TR. + * @override + */ +goog.ui.DrilldownRow.prototype.canDecorate = function(node) { + return node.tagName == goog.dom.TagName.TR; +}; + + +/** + * Child drilldowns are rendered when needed. + * + * @param {goog.ui.Component} child New DrilldownRow child to be added. + * @param {number} index position to be occupied by the child. + * @param {boolean=} opt_render true to force immediate rendering. + * @override + */ +goog.ui.DrilldownRow.prototype.addChildAt = function(child, index, opt_render) { + goog.asserts.assertInstanceof(child, goog.ui.DrilldownRow); + goog.ui.DrilldownRow.superClass_.addChildAt.call(this, child, index, false); + child.setDisplayable_(this.isVisible_() && this.isExpanded()); + if (opt_render && !child.isInDocument()) { + child.render(); + } +}; + + +/** @override */ +goog.ui.DrilldownRow.prototype.removeChild = function(child) { + goog.dom.removeNode(child.getElement()); + return goog.ui.DrilldownRow.superClass_.removeChild.call(this, child); +}; + + +/** + * Rendering of DrilldownRow's is on need, do not call this directly + * from application code. + * + * Rendering a DrilldownRow places it according to its position in its + * tree of DrilldownRows. DrilldownRows cannot be placed any other + * way so this method does not use any arguments. This does not call + * the base class method and does not modify any of this + * DrilldownRow's children. + * @override + */ +goog.ui.DrilldownRow.prototype.render = function() { + if (arguments.length) { + throw new Error('A DrilldownRow cannot be placed under a specific parent.'); + } else { + var parent = this.getParent(); + if (!parent.isInDocument()) { + throw new Error('Cannot render child of un-rendered parent'); + } + // The new child's TR node needs to go just after the last TR + // of the part of the parent's subtree that is to the left + // of this. The subtree includes the parent. + goog.asserts.assertInstanceof(parent, goog.ui.DrilldownRow); + var previous = parent.previousRenderedChild_(this); + var row; + if (previous) { + goog.asserts.assertInstanceof(previous, goog.ui.DrilldownRow); + row = previous.lastRenderedLeaf_().getElement(); + } else { + row = parent.getElement(); + } + row = /** @type {Element} */ (row.nextSibling); + // Render the child row component into the document. + if (row) { + this.renderBefore(row); + } else { + // Render at the end of the parent of this DrilldownRow's + // DOM element. + var tbody = /** @type {Element} */ (parent.getElement().parentNode); + goog.ui.DrilldownRow.superClass_.render.call(this, tbody); + } + } +}; + + +/** + * Finds the numeric index of this child within its parent Component. + * Throws an exception if it has no parent. + * + * @return {number} index of this within the children of the parent Component. + */ +goog.ui.DrilldownRow.prototype.findIndex = function() { + var parent = this.getParent(); + if (!parent) { + throw new Error('Component has no parent'); + } + return parent.indexOfChild(this); +}; + + +// +// Type-specific operations +// + + +/** + * Returns the expanded state of the DrilldownRow. + * + * @return {boolean} true iff this is expanded. + */ +goog.ui.DrilldownRow.prototype.isExpanded = function() { + return this.expanded_; +}; + + +/** + * Sets the expanded state of this DrilldownRow: makes all children + * displayable or not displayable corresponding to the expanded state. + * + * @param {boolean} expanded whether this should be expanded or not. + */ +goog.ui.DrilldownRow.prototype.setExpanded = function(expanded) { + if (expanded != this.expanded_) { + this.expanded_ = expanded; + var elem = this.getElement(); + goog.asserts.assert(elem); + goog.dom.classlist.toggle(elem, goog.getCssName('goog-drilldown-expanded')); + goog.dom.classlist.toggle( + elem, goog.getCssName('goog-drilldown-collapsed')); + if (this.isVisible_()) { + this.forEachChild(function(child) { child.setDisplayable_(expanded); }); + } + } +}; + + +/** + * Returns this DrilldownRow's level in the tree. Top level is 1. + * + * @return {number} depth of this DrilldownRow in its tree of drilldowns. + */ +goog.ui.DrilldownRow.prototype.getDepth = function() { + for (var component = this, depth = 0; + component instanceof goog.ui.DrilldownRow; + component = component.getParent(), depth++) { + } + return depth; +}; + + +/** + * This static function is a default decorator that adds HTML at the + * beginning of the first cell to display indentation and an expander + * image; sets up a click handler on the toggler; initializes a class + * for the row: either goog-drilldown-expanded or + * goog-drilldown-collapsed, depending on the initial state of the + * DrilldownRow; and sets up a click event handler on the toggler + * element. + * + * This creates a DIV with class=toggle. Your application can set up + * CSS style rules something like this: + * + * tr.goog-drilldown-expanded .toggle { + * background-image: url('minus.png'); + * } + * + * tr.goog-drilldown-collapsed .toggle { + * background-image: url('plus.png'); + * } + * + * These background images show whether the DrilldownRow is expanded. + * @param {goog.ui.DrilldownRow} selfObj DrilldownRow to be decorated. + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.DrilldownRow.decorate = function(selfObj) { + var depth = selfObj.getDepth(); + var row = selfObj.getElement(); + goog.asserts.assert(row); + if (!row.cells) { + throw new Error('No cells'); + } + var cell = row.cells[0]; + var dom = selfObj.getDomHelper(); + var fragment = dom.createDom( + goog.dom.TagName.DIV, {'style': 'float: left; width: ' + depth + 'em;'}, + dom.createDom( + goog.dom.TagName.DIV, + {'class': 'toggle', 'style': 'width: 1em; float: right;'}, + // NOTE: NBSP is probably only needed by IE6. This div can probably be + // made contentless. + goog.string.Unicode.NBSP)); + cell.insertBefore(fragment, cell.firstChild); + goog.dom.classlist.add( + row, selfObj.isExpanded() ? goog.getCssName('goog-drilldown-expanded') : + goog.getCssName('goog-drilldown-collapsed')); + // Default mouse event handling: + var toggler = + goog.dom.getElementsByTagName(goog.dom.TagName.DIV, fragment)[0]; + selfObj.getHandler().listen(toggler, 'click', function(event) { + selfObj.setExpanded(!selfObj.isExpanded()); + }); +}; + + +// +// Private methods +// + + +/** + * Turn display of a DrilldownRow on or off. If the DrilldownRow has not + * yet been rendered, this renders it. This propagates the effect + * of the change recursively as needed -- children displaying iff the + * parent is displayed and expanded. + * + * @param {boolean} display state, true iff display is desired. + * @private + */ +goog.ui.DrilldownRow.prototype.setDisplayable_ = function(display) { + if (display && !this.isInDocument()) { + this.render(); + } + if (this.displayed_ == display) { + return; + } + this.displayed_ = display; + if (this.isInDocument()) { + this.getElement().style.display = display ? '' : 'none'; + } + var selfObj = this; + this.forEachChild(function(child) { + child.setDisplayable_(display && selfObj.expanded_); + }); +}; + + +/** + * True iff this and all its DrilldownRow parents are displayable. The + * value is an approximation to actual visibility, since it does not + * look at whether DOM nodes containing the top-level component have + * display: none, visibility: hidden or are otherwise not displayable. + * So this visibility is relative to the top-level component. + * + * @return {boolean} visibility of this relative to its top-level drilldown. + * @private + */ +goog.ui.DrilldownRow.prototype.isVisible_ = function() { + for (var component = this; component instanceof goog.ui.DrilldownRow; + component = component.getParent()) { + if (!component.displayed_) return false; + } + return true; +}; + + +/** + * Create and return a TR element from HTML that looks like + * " ... ". + * @param {!goog.html.SafeHtml} html for one row. + * @param {!goog.dom.DomHelper} dom DOM to hold the Element. + * @return {Element} table row node created from the HTML. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.DrilldownRow.createRowNode_ = function(html, dom) { + // Note: this may be slow. + var tableHtml = goog.html.SafeHtml.create(goog.dom.TagName.TABLE, {}, html); + var div = dom.createElement(goog.dom.TagName.DIV); + goog.dom.safe.setInnerHtml(div, tableHtml); + return div.firstChild.rows[0]; +}; + + +/** + * Get the recursively rightmost child that is in the document. + * + * @return {goog.ui.DrilldownRow} rightmost child currently entered in + * the document, potentially this DrilldownRow. If this is in the + * document, result is non-null. + * @private + */ +goog.ui.DrilldownRow.prototype.lastRenderedLeaf_ = function() { + var leaf = null; + for (var node = this; node && node.isInDocument(); + // Node will become undefined if parent has no children. + node = node.getChildAt(node.getChildCount() - 1)) { + leaf = node; + } + return /** @type {goog.ui.DrilldownRow} */ (leaf); +}; + + +/** + * Search this node's direct children for the last one that is in the + * document and is before the given child. + * @param {goog.ui.DrilldownRow} child The child to stop the search at. + * @return {goog.ui.Component?} The last child component before the given child + * that is in the document. + * @private + */ +goog.ui.DrilldownRow.prototype.previousRenderedChild_ = function(child) { + for (var i = this.getChildCount() - 1; i >= 0; i--) { + if (this.getChildAt(i) == child) { + for (var j = i - 1; j >= 0; j--) { + var prev = this.getChildAt(j); + if (prev.isInDocument()) { + return prev; + } + } + } + } + return null; +}; diff --git a/closure-library/closure/goog/ui/editor/abstractdialog.js b/closure-library/closure/goog/ui/editor/abstractdialog.js new file mode 100644 index 0000000000..a6bdc3fc29 --- /dev/null +++ b/closure-library/closure/goog/ui/editor/abstractdialog.js @@ -0,0 +1,440 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Wrapper around {@link goog.ui.Dialog}, to provide + * dialogs that are smarter about interacting with a rich text editor. + * + * @author nicksantos@google.com (Nick Santos) + */ + +goog.provide('goog.ui.editor.AbstractDialog'); +goog.provide('goog.ui.editor.AbstractDialog.Builder'); +goog.provide('goog.ui.editor.AbstractDialog.EventType'); + +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.classlist'); +goog.require('goog.events.EventTarget'); +goog.require('goog.string'); +goog.require('goog.ui.Dialog'); +goog.require('goog.ui.PopupBase'); + + +// *** Public interface ***************************************************** // + + + +/** + * Creates an object that represents a dialog box. + * @param {goog.dom.DomHelper} domHelper DomHelper to be used to create the + * dialog's dom structure. + * @constructor + * @extends {goog.events.EventTarget} + */ +goog.ui.editor.AbstractDialog = function(domHelper) { + goog.ui.editor.AbstractDialog.base(this, 'constructor'); + this.dom = domHelper; + + /** @private {?goog.ui.Dialog} */ + this.dialogInternal_ = null; +}; +goog.inherits(goog.ui.editor.AbstractDialog, goog.events.EventTarget); + + +/** + * Causes the dialog box to appear, centered on the screen. Lazily creates the + * dialog if needed. + */ +goog.ui.editor.AbstractDialog.prototype.show = function() { + // Lazily create the wrapped dialog to be shown. + if (!this.dialogInternal_) { + this.dialogInternal_ = this.createDialogControl(); + this.dialogInternal_.listen( + goog.ui.PopupBase.EventType.HIDE, this.handleAfterHide_, false, this); + } + + this.dialogInternal_.setVisible(true); +}; + + +/** + * Hides the dialog, causing AFTER_HIDE to fire. + */ +goog.ui.editor.AbstractDialog.prototype.hide = function() { + if (this.dialogInternal_) { + // This eventually fires the wrapped dialog's AFTER_HIDE event, calling our + // handleAfterHide_(). + this.dialogInternal_.setVisible(false); + } +}; + + +/** + * @return {boolean} Whether the dialog is open. + */ +goog.ui.editor.AbstractDialog.prototype.isOpen = function() { + return !!this.dialogInternal_ && this.dialogInternal_.isVisible(); +}; + + +/** + * Runs the handler registered on the OK button event and closes the dialog if + * that handler succeeds. + * This is useful in cases such as double-clicking an item in the dialog is + * equivalent to selecting it and clicking the default button. + * @protected + */ +goog.ui.editor.AbstractDialog.prototype.processOkAndClose = function() { + // Fake an OK event from the wrapped dialog control. + var evt = new goog.ui.Dialog.Event(goog.ui.Dialog.DefaultButtonKeys.OK, null); + if (this.handleOk(evt)) { + // handleOk calls dispatchEvent, so if any listener calls preventDefault it + // will return false and we won't hide the dialog. + this.hide(); + } +}; + + +// *** Dialog events ******************************************************** // + + +/** + * Event type constants for events the dialog fires. + * @enum {string} + */ +goog.ui.editor.AbstractDialog.EventType = { + // This event is fired after the dialog is hidden, no matter if it was closed + // via OK or Cancel or is being disposed without being hidden first. + AFTER_HIDE: 'afterhide', + // Either the cancel or OK events can be canceled via preventDefault or by + // returning false from their handlers to stop the dialog from closing. + CANCEL: 'cancel', + OK: 'ok' +}; + + +// *** Inner helper class *************************************************** // + + + +/** + * A builder class for the dialog control. All methods except build return this. + * @param {goog.ui.editor.AbstractDialog} editorDialog Editor dialog object + * that will wrap the wrapped dialog object this builder will create. + * @constructor + */ +goog.ui.editor.AbstractDialog.Builder = function(editorDialog) { + // We require the editor dialog to be passed in so that the builder can set up + // ok/cancel listeners by default, making it easier for most dialogs. + this.editorDialog_ = editorDialog; + this.wrappedDialog_ = new goog.ui.Dialog('', true, this.editorDialog_.dom); + this.buttonSet_ = new goog.ui.Dialog.ButtonSet(this.editorDialog_.dom); + this.buttonHandlers_ = {}; + this.addClassName(goog.getCssName('tr-dialog')); +}; + + +/** + * Sets the title of the dialog. + * @param {string} title Title HTML (escaped). + * @return {!goog.ui.editor.AbstractDialog.Builder} This. + */ +goog.ui.editor.AbstractDialog.Builder.prototype.setTitle = function(title) { + this.wrappedDialog_.setTitle(title); + return this; +}; + + +/** + * Adds an OK button to the dialog. Clicking this button will cause {@link + * handleOk} to run, subsequently dispatching an OK event. + * @param {string=} opt_label The caption for the button, if not "OK". + * @return {!goog.ui.editor.AbstractDialog.Builder} This. + */ +goog.ui.editor.AbstractDialog.Builder.prototype.addOkButton = function( + opt_label) { + var key = goog.ui.Dialog.DefaultButtonKeys.OK; + /** @desc Label for an OK button in an editor dialog. */ + var MSG_TR_DIALOG_OK = goog.getMsg('OK'); + // True means this is the default/OK button. + this.buttonSet_.set(key, opt_label || MSG_TR_DIALOG_OK, true); + this.buttonHandlers_[key] = + goog.bind(this.editorDialog_.handleOk, this.editorDialog_); + return this; +}; + + +/** + * Adds a Cancel button to the dialog. Clicking this button will cause {@link + * handleCancel} to run, subsequently dispatching a CANCEL event. + * @param {string=} opt_label The caption for the button, if not "Cancel". + * @return {!goog.ui.editor.AbstractDialog.Builder} This. + */ +goog.ui.editor.AbstractDialog.Builder.prototype.addCancelButton = function( + opt_label) { + var key = goog.ui.Dialog.DefaultButtonKeys.CANCEL; + /** @desc Label for a cancel button in an editor dialog. */ + var MSG_TR_DIALOG_CANCEL = goog.getMsg('Cancel'); + // False means it's not the OK button, true means it's the Cancel button. + this.buttonSet_.set(key, opt_label || MSG_TR_DIALOG_CANCEL, false, true); + this.buttonHandlers_[key] = + goog.bind(this.editorDialog_.handleCancel, this.editorDialog_); + return this; +}; + + +/** + * Adds a custom button to the dialog. + * @param {string} label The caption for the button. + * @param {function(goog.ui.Dialog.EventType):*} handler Function called when + * the button is clicked. It is recommended that this function be a method + * in the concrete subclass of AbstractDialog using this Builder, and that + * it dispatch an event (see {@link handleOk}). + * @param {string=} opt_buttonId Identifier to be used to access the button when + * calling AbstractDialog.getButtonElement(). + * @return {!goog.ui.editor.AbstractDialog.Builder} This. + */ +goog.ui.editor.AbstractDialog.Builder.prototype.addButton = function( + label, handler, opt_buttonId) { + // We don't care what the key is, just that we can match the button with the + // handler function later. + var key = opt_buttonId || goog.string.createUniqueString(); + this.buttonSet_.set(key, label); + this.buttonHandlers_[key] = handler; + return this; +}; + + +/** + * Puts a CSS class on the dialog's main element. + * @param {string} className The class to add. + * @return {!goog.ui.editor.AbstractDialog.Builder} This. + */ +goog.ui.editor.AbstractDialog.Builder.prototype.addClassName = function( + className) { + goog.dom.classlist.add( + goog.asserts.assert(this.wrappedDialog_.getDialogElement()), className); + return this; +}; + + +/** + * Sets the content element of the dialog. + * @param {Element} contentElem An element for the main body. + * @return {!goog.ui.editor.AbstractDialog.Builder} This. + */ +goog.ui.editor.AbstractDialog.Builder.prototype.setContent = function( + contentElem) { + goog.dom.appendChild(this.wrappedDialog_.getContentElement(), contentElem); + return this; +}; + + +/** + * Builds the wrapped dialog control. May only be called once, after which + * no more methods may be called on this builder. + * @return {!goog.ui.Dialog} The wrapped dialog control. + */ +goog.ui.editor.AbstractDialog.Builder.prototype.build = function() { + if (this.buttonSet_.isEmpty()) { + // If caller didn't set any buttons, add an OK and Cancel button by default. + this.addOkButton(); + this.addCancelButton(); + } + this.wrappedDialog_.setButtonSet(this.buttonSet_); + + var handlers = this.buttonHandlers_; + this.buttonHandlers_ = null; + this.wrappedDialog_.listen( + goog.ui.Dialog.EventType.SELECT, + // Listen for the SELECT event, which means a button was clicked, and + // call the handler associated with that button via the key property. + function(e) { + if (handlers[e.key]) { + return handlers[e.key](e); + } + }); + + // All editor dialogs are modal. + this.wrappedDialog_.setModal(true); + + var dialog = this.wrappedDialog_; + this.wrappedDialog_ = null; + return dialog; +}; + + +/** + * Editor dialog that will wrap the wrapped dialog this builder will create. + * @type {goog.ui.editor.AbstractDialog} + * @private + */ +goog.ui.editor.AbstractDialog.Builder.prototype.editorDialog_; + + +/** + * wrapped dialog control being built by this builder. + * @type {goog.ui.Dialog} + * @private + */ +goog.ui.editor.AbstractDialog.Builder.prototype.wrappedDialog_; + + +/** + * Set of buttons to be added to the wrapped dialog control. + * @type {goog.ui.Dialog.ButtonSet} + * @private + */ +goog.ui.editor.AbstractDialog.Builder.prototype.buttonSet_; + + +/** + * Map from keys that will be returned in the wrapped dialog SELECT events to + * handler functions to be called to handle those events. + * @type {Object} + * @private + */ +goog.ui.editor.AbstractDialog.Builder.prototype.buttonHandlers_; + + +// *** Protected interface ************************************************** // + + +/** + * The DOM helper for the parent document. + * @type {goog.dom.DomHelper} + * @protected + */ +goog.ui.editor.AbstractDialog.prototype.dom; + + +/** + * Creates and returns the goog.ui.Dialog control that is being wrapped + * by this object. + * @return {!goog.ui.Dialog} Created Dialog control. + * @protected + */ +goog.ui.editor.AbstractDialog.prototype.createDialogControl = + goog.abstractMethod; + + +/** + * Returns the HTML Button element for the OK button in this dialog. + * @return {Element} The button element if found, else null. + * @protected + */ +goog.ui.editor.AbstractDialog.prototype.getOkButtonElement = function() { + return this.getButtonElement(goog.ui.Dialog.DefaultButtonKeys.OK); +}; + + +/** + * Returns the HTML Button element for the Cancel button in this dialog. + * @return {Element} The button element if found, else null. + * @protected + */ +goog.ui.editor.AbstractDialog.prototype.getCancelButtonElement = function() { + return this.getButtonElement(goog.ui.Dialog.DefaultButtonKeys.CANCEL); +}; + + +/** + * Returns the HTML Button element for the button added to this dialog with + * the given button id. + * @param {string} buttonId The id of the button to get. + * @return {Element} The button element if found, else null. + * @protected + */ +goog.ui.editor.AbstractDialog.prototype.getButtonElement = function(buttonId) { + return this.dialogInternal_.getButtonSet().getButton(buttonId); +}; + + +/** + * Creates and returns the event object to be used when dispatching the OK + * event to listeners, or returns null to prevent the dialog from closing. + * Subclasses should override this to return their own subclass of + * goog.events.Event that includes all data a plugin would need from the dialog. + * @param {goog.events.Event} e The event object dispatched by the wrapped + * dialog. + * @return {goog.events.Event} The event object to be used when dispatching the + * OK event to listeners. + * @protected + */ +goog.ui.editor.AbstractDialog.prototype.createOkEvent = goog.abstractMethod; + + +/** + * Handles the event dispatched by the wrapped dialog control when the user + * clicks the OK button. Attempts to create the OK event object and dispatches + * it if successful. + * @param {goog.ui.Dialog.Event} e wrapped dialog OK event object. + * @return {boolean} Whether the default action (closing the dialog) should + * still be executed. This will be false if the OK event could not be + * created to be dispatched, or if any listener to that event returs false + * or calls preventDefault. + * @protected + */ +goog.ui.editor.AbstractDialog.prototype.handleOk = function(e) { + var eventObj = this.createOkEvent(e); + if (eventObj) { + return this.dispatchEvent(eventObj); + } else { + return false; + } +}; + + +/** + * Handles the event dispatched by the wrapped dialog control when the user + * clicks the Cancel button. Simply dispatches a CANCEL event. + * @return {boolean} Returns false if any of the handlers called prefentDefault + * on the event or returned false themselves. + * @protected + */ +goog.ui.editor.AbstractDialog.prototype.handleCancel = function() { + return this.dispatchEvent(goog.ui.editor.AbstractDialog.EventType.CANCEL); +}; + + +/** + * Disposes of the dialog. If the dialog is open, it will be hidden and + * AFTER_HIDE will be dispatched. + * @override + * @protected + */ +goog.ui.editor.AbstractDialog.prototype.disposeInternal = function() { + if (this.dialogInternal_) { + this.hide(); + + this.dialogInternal_.dispose(); + this.dialogInternal_ = null; + } + + goog.ui.editor.AbstractDialog.superClass_.disposeInternal.call(this); +}; + + +// *** Private implementation *********************************************** // + + +/** + * Cleans up after the dialog is hidden and fires the AFTER_HIDE event. Should + * be a listener for the wrapped dialog's AFTER_HIDE event. + * @private + */ +goog.ui.editor.AbstractDialog.prototype.handleAfterHide_ = function() { + this.dispatchEvent(goog.ui.editor.AbstractDialog.EventType.AFTER_HIDE); +}; diff --git a/closure-library/closure/goog/ui/editor/bubble.js b/closure-library/closure/goog/ui/editor/bubble.js new file mode 100644 index 0000000000..01c7299d5a --- /dev/null +++ b/closure-library/closure/goog/ui/editor/bubble.js @@ -0,0 +1,563 @@ +// Copyright 2005 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Bubble component - handles display, hiding, etc. of the + * actual bubble UI. + * + * This is used exclusively by code within the editor package, and should not + * be used directly. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.ui.editor.Bubble'); + +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.ViewportSizeMonitor'); +goog.require('goog.dom.classlist'); +goog.require('goog.editor.style'); +goog.require('goog.events.EventHandler'); +goog.require('goog.events.EventTarget'); +goog.require('goog.events.EventType'); +goog.require('goog.functions'); +goog.require('goog.log'); +goog.require('goog.math.Box'); +goog.require('goog.object'); +goog.require('goog.positioning'); +goog.require('goog.positioning.Corner'); +goog.require('goog.positioning.Overflow'); +goog.require('goog.positioning.OverflowStatus'); +goog.require('goog.string'); +goog.require('goog.style'); +goog.require('goog.ui.Component'); +goog.require('goog.ui.PopupBase'); +goog.require('goog.userAgent'); + + + +/** + * Property bubble UI element. + * @param {Element} parent The parent element for this bubble. + * @param {number} zIndex The z index to draw the bubble at. + * @constructor + * @extends {goog.events.EventTarget} + */ +goog.ui.editor.Bubble = function(parent, zIndex) { + goog.ui.editor.Bubble.base(this, 'constructor'); + + /** + * Dom helper for the document the bubble should be shown in. + * @type {!goog.dom.DomHelper} + * @private + */ + this.dom_ = goog.dom.getDomHelper(parent); + + /** + * Event handler for this bubble. + * @type {goog.events.EventHandler} + * @private + */ + this.eventHandler_ = new goog.events.EventHandler(this); + + /** + * Object that monitors the application window for size changes. + * @type {goog.dom.ViewportSizeMonitor} + * @private + */ + this.viewPortSizeMonitor_ = + new goog.dom.ViewportSizeMonitor(this.dom_.getWindow()); + + /** + * Maps panel ids to panels. + * @type {Object} + * @private + */ + this.panels_ = {}; + + /** + * Container element for the entire bubble. This may contain elements related + * to look and feel or styling of the bubble. + * @type {Element} + * @private + */ + this.bubbleContainer_ = this.dom_.createDom( + goog.dom.TagName.DIV, + {'className': goog.ui.editor.Bubble.BUBBLE_CLASSNAME}); + + goog.style.setElementShown(this.bubbleContainer_, false); + goog.dom.appendChild(parent, this.bubbleContainer_); + goog.style.setStyle(this.bubbleContainer_, 'zIndex', zIndex); + + /** + * Container element for the bubble panels - this should be some inner element + * within (or equal to) bubbleContainer. + * @type {Element} + * @private + */ + this.bubbleContents_ = this.createBubbleDom(this.dom_, this.bubbleContainer_); + + /** + * Element showing the close box. + * @type {!Element} + * @private + */ + this.closeBox_ = this.dom_.createDom(goog.dom.TagName.DIV, { + 'className': goog.getCssName('tr_bubble_closebox'), + 'innerHTML': ' ' + }); + this.bubbleContents_.appendChild(this.closeBox_); + + // We make bubbles unselectable so that clicking on them does not steal focus + // or move the cursor away from the element the bubble is attached to. + goog.editor.style.makeUnselectable(this.bubbleContainer_, this.eventHandler_); + + /** + * Popup that controls showing and hiding the bubble at the appropriate + * position. + * @type {goog.ui.PopupBase} + * @private + */ + this.popup_ = new goog.ui.PopupBase(this.bubbleContainer_); +}; +goog.inherits(goog.ui.editor.Bubble, goog.events.EventTarget); + + +/** + * The css class name of the bubble container element. + * @type {string} + */ +goog.ui.editor.Bubble.BUBBLE_CLASSNAME = goog.getCssName('tr_bubble'); + + +/** + * Creates and adds DOM for the bubble UI to the given container. This default + * implementation just returns the container itself. + * @param {!goog.dom.DomHelper} dom DOM helper to use. + * @param {!Element} container Element to add the new elements to. + * @return {!Element} The element where bubble content should be added. + * @protected + */ +goog.ui.editor.Bubble.prototype.createBubbleDom = function(dom, container) { + return container; +}; + + +/** + * A logger for goog.ui.editor.Bubble. + * @type {goog.log.Logger} + * @protected + */ +goog.ui.editor.Bubble.prototype.logger = + goog.log.getLogger('goog.ui.editor.Bubble'); + + +/** @override */ +goog.ui.editor.Bubble.prototype.disposeInternal = function() { + goog.ui.editor.Bubble.base(this, 'disposeInternal'); + + goog.dom.removeNode(this.bubbleContainer_); + this.bubbleContainer_ = null; + + this.eventHandler_.dispose(); + this.eventHandler_ = null; + + this.viewPortSizeMonitor_.dispose(); + this.viewPortSizeMonitor_ = null; +}; + + +/** + * @return {Element} The element that where the bubble's contents go. + */ +goog.ui.editor.Bubble.prototype.getContentElement = function() { + return this.bubbleContents_; +}; + + +/** + * @return {Element} The element that contains the bubble. + * @protected + */ +goog.ui.editor.Bubble.prototype.getContainerElement = function() { + return this.bubbleContainer_; +}; + + +/** + * @return {goog.events.EventHandler} The event handler. + * @protected + * @this {T} + * @template T + */ +goog.ui.editor.Bubble.prototype.getEventHandler = function() { + return this.eventHandler_; +}; + + +/** + * Handles user resizing of window. + * @private + */ +goog.ui.editor.Bubble.prototype.handleWindowResize_ = function() { + if (this.isVisible()) { + this.reposition(); + } +}; + + +/** + * Sets whether the bubble dismisses itself when the user clicks outside of it. + * @param {boolean} autoHide Whether to autohide on an external click. + */ +goog.ui.editor.Bubble.prototype.setAutoHide = function(autoHide) { + this.popup_.setAutoHide(autoHide); +}; + + +/** + * Returns whether there is already a panel of the given type. + * @param {string} type Type of panel to check. + * @return {boolean} Whether there is already a panel of the given type. + */ +goog.ui.editor.Bubble.prototype.hasPanelOfType = function(type) { + return goog.object.some( + this.panels_, function(panel) { return panel.type == type; }); +}; + + +/** + * Adds a panel to the bubble. + * @param {string} type The type of bubble panel this is. Should usually be + * the same as the tagName of the targetElement. This ensures multiple + * bubble panels don't appear for the same element. + * @param {string} title The title of the panel. + * @param {Element} targetElement The target element of the bubble. + * @param {function(Element): void} contentFn Function that when called with + * a container element, will add relevant panel content to it. + * @param {boolean=} opt_preferTopPosition Whether to prefer placing the bubble + * above the element instead of below it. Defaults to preferring below. + * If any panel prefers the top position, the top position is used. + * @return {string} The id of the panel. + */ +goog.ui.editor.Bubble.prototype.addPanel = function( + type, title, targetElement, contentFn, opt_preferTopPosition) { + var id = goog.string.createUniqueString(); + var panel = new goog.ui.editor.Bubble.Panel_( + this.dom_, id, type, title, targetElement, !opt_preferTopPosition); + this.panels_[id] = panel; + + // Insert the panel in string order of type. Technically we could use binary + // search here but n is really small (probably 0 - 2) so it's not worth it. + // The last child of bubbleContents_ is the close box so we take care not + // to treat it as a panel element, and we also ensure it stays as the last + // element. The intention here is not to create any artificial order, but + // just to ensure that it is always consistent. + var nextElement; + for (var i = 0, len = this.bubbleContents_.childNodes.length - 1; i < len; + i++) { + var otherChild = this.bubbleContents_.childNodes[i]; + var otherPanel = this.panels_[otherChild.id]; + if (otherPanel.type > type) { + nextElement = otherChild; + break; + } + } + goog.dom.insertSiblingBefore( + panel.element, nextElement || this.bubbleContents_.lastChild); + + contentFn(panel.getContentElement()); + goog.editor.style.makeUnselectable(panel.element, this.eventHandler_); + + var numPanels = goog.object.getCount(this.panels_); + if (numPanels == 1) { + this.openBubble_(); + } else if (numPanels == 2) { + goog.dom.classlist.add( + goog.asserts.assert(this.bubbleContainer_), + goog.getCssName('tr_multi_bubble')); + } + this.reposition(); + + return id; +}; + + +/** + * Removes the panel with the given id. + * @param {string} id The id of the panel. + */ +goog.ui.editor.Bubble.prototype.removePanel = function(id) { + var panel = this.panels_[id]; + goog.dom.removeNode(panel.element); + delete this.panels_[id]; + + var numPanels = goog.object.getCount(this.panels_); + if (numPanels <= 1) { + goog.dom.classlist.remove( + goog.asserts.assert(this.bubbleContainer_), + goog.getCssName('tr_multi_bubble')); + } + + if (numPanels == 0) { + this.closeBubble_(); + } else { + this.reposition(); + } +}; + + +/** + * Opens the bubble. + * @private + */ +goog.ui.editor.Bubble.prototype.openBubble_ = function() { + this.eventHandler_ + .listen(this.closeBox_, goog.events.EventType.CLICK, this.closeBubble_) + .listen( + this.viewPortSizeMonitor_, goog.events.EventType.RESIZE, + this.handleWindowResize_) + .listen( + this.popup_, goog.ui.PopupBase.EventType.HIDE, this.handlePopupHide); + + this.popup_.setVisible(true); + this.reposition(); +}; + + +/** + * Closes the bubble. + * @private + */ +goog.ui.editor.Bubble.prototype.closeBubble_ = function() { + this.popup_.setVisible(false); +}; + + +/** + * Handles the popup's hide event by removing all panels and dispatching a + * HIDE event. + * @protected + */ +goog.ui.editor.Bubble.prototype.handlePopupHide = function() { + // Remove the panel elements. + for (var panelId in this.panels_) { + goog.dom.removeNode(this.panels_[panelId].element); + } + + // Update the state to reflect no panels. + this.panels_ = {}; + goog.dom.classlist.remove( + goog.asserts.assert(this.bubbleContainer_), + goog.getCssName('tr_multi_bubble')); + + this.eventHandler_.removeAll(); + this.dispatchEvent(goog.ui.Component.EventType.HIDE); +}; + + +/** + * Returns the visibility of the bubble. + * @return {boolean} True if visible false if not. + */ +goog.ui.editor.Bubble.prototype.isVisible = function() { + return this.popup_.isVisible(); +}; + + +/** + * The vertical clearance in pixels between the bottom of the targetElement + * and the edge of the bubble. + * @type {number} + * @private + */ +goog.ui.editor.Bubble.VERTICAL_CLEARANCE_ = goog.userAgent.IE ? 4 : 2; + + +/** + * Bubble's margin box to be passed to goog.positioning. + * @type {goog.math.Box} + * @private + */ +goog.ui.editor.Bubble.MARGIN_BOX_ = new goog.math.Box( + goog.ui.editor.Bubble.VERTICAL_CLEARANCE_, 0, + goog.ui.editor.Bubble.VERTICAL_CLEARANCE_, 0); + + +/** + * Returns the margin box. + * @return {goog.math.Box} + * @protected + */ +goog.ui.editor.Bubble.prototype.getMarginBox = function() { + return goog.ui.editor.Bubble.MARGIN_BOX_; +}; + + +/** + * Positions and displays this bubble below its targetElement. Assumes that + * the bubbleContainer is already contained in the document object it applies + * to. + */ +goog.ui.editor.Bubble.prototype.reposition = function() { + var targetElement = null; + var preferBottomPosition = true; + for (var panelId in this.panels_) { + var panel = this.panels_[panelId]; + // We don't care which targetElement we get, so we just take the last one. + targetElement = panel.targetElement; + preferBottomPosition = preferBottomPosition && panel.preferBottomPosition; + } + var status = goog.positioning.OverflowStatus.FAILED; + + // Fix for bug when bubbleContainer and targetElement have + // opposite directionality, the bubble should anchor to the END of + // the targetElement instead of START. + var reverseLayout = + (goog.style.isRightToLeft(this.bubbleContainer_) != + goog.style.isRightToLeft(targetElement)); + + // Try to put the bubble at the bottom of the target unless the plugin has + // requested otherwise. + if (preferBottomPosition) { + status = this.positionAtAnchor_( + reverseLayout ? goog.positioning.Corner.BOTTOM_END : + goog.positioning.Corner.BOTTOM_START, + goog.positioning.Corner.TOP_START, + goog.positioning.Overflow.ADJUST_X | goog.positioning.Overflow.FAIL_Y); + } + + if (status & goog.positioning.OverflowStatus.FAILED) { + // Try to put it at the top of the target if there is not enough + // space at the bottom. + status = this.positionAtAnchor_( + reverseLayout ? goog.positioning.Corner.TOP_END : + goog.positioning.Corner.TOP_START, + goog.positioning.Corner.BOTTOM_START, + goog.positioning.Overflow.ADJUST_X | goog.positioning.Overflow.FAIL_Y); + } + + if (status & goog.positioning.OverflowStatus.FAILED) { + // Put it at the bottom again with adjustment if there is no + // enough space at the top. + status = this.positionAtAnchor_( + reverseLayout ? goog.positioning.Corner.BOTTOM_END : + goog.positioning.Corner.BOTTOM_START, + goog.positioning.Corner.TOP_START, goog.positioning.Overflow.ADJUST_X | + goog.positioning.Overflow.ADJUST_Y); + if (status & goog.positioning.OverflowStatus.FAILED) { + goog.log.warning( + this.logger, + 'reposition(): positionAtAnchor() failed with ' + status); + } + } +}; + + +/** + * A helper for reposition() - positions the bubble in regards to the position + * of the elements the bubble is attached to. + * @param {goog.positioning.Corner} targetCorner The corner of + * the target element. + * @param {goog.positioning.Corner} bubbleCorner The corner of the bubble. + * @param {number} overflow Overflow handling mode bitmap, + * {@see goog.positioning.Overflow}. + * @return {number} Status bitmap, {@see goog.positioning.OverflowStatus}. + * @private + */ +goog.ui.editor.Bubble.prototype.positionAtAnchor_ = function( + targetCorner, bubbleCorner, overflow) { + var targetElement = null; + for (var panelId in this.panels_) { + // For now, we use the outermost element. This assumes the multiple + // elements this panel is showing for contain each other - in the event + // that is not generally the case this may need to be updated to pick + // the lowest or highest element depending on targetCorner. + var candidate = this.panels_[panelId].targetElement; + if (!targetElement || goog.dom.contains(candidate, targetElement)) { + targetElement = this.panels_[panelId].targetElement; + } + } + return goog.positioning.positionAtAnchor( + targetElement, targetCorner, this.bubbleContainer_, bubbleCorner, null, + this.getMarginBox(), overflow, null, this.getViewportBox()); +}; + + +/** + * Returns the viewport box to use when positioning the bubble. + * @return {goog.math.Box} + * @protected + */ +goog.ui.editor.Bubble.prototype.getViewportBox = goog.functions.NULL; + + + +/** + * Private class used to describe a bubble panel. + * @param {goog.dom.DomHelper} dom DOM helper used to create the panel. + * @param {string} id ID of the panel. + * @param {string} type Type of the panel. + * @param {string} title Title of the panel. + * @param {Element} targetElement Element the panel is showing for. + * @param {boolean} preferBottomPosition Whether this panel prefers to show + * below the target element. + * @constructor + * @private + */ +goog.ui.editor.Bubble.Panel_ = function( + dom, id, type, title, targetElement, preferBottomPosition) { + /** + * The type of bubble panel. + * @type {string} + */ + this.type = type; + + /** + * The target element of this bubble panel. + * @type {Element} + */ + this.targetElement = targetElement; + + /** + * Whether the panel prefers to be placed below the target element. + * @type {boolean} + */ + this.preferBottomPosition = preferBottomPosition; + + /** + * The element containing this panel. + * @type {!Element} + */ + this.element = dom.createDom( + goog.dom.TagName.DIV, + {className: goog.getCssName('tr_bubble_panel'), id: id}, + dom.createDom( + goog.dom.TagName.DIV, + {className: goog.getCssName('tr_bubble_panel_title')}, + title ? title + ':' : ''), // TODO(robbyw): Does this work in bidi? + dom.createDom( + goog.dom.TagName.DIV, + {className: goog.getCssName('tr_bubble_panel_content')})); +}; + + +/** + * @return {Element} The element in the panel where content should go. + */ +goog.ui.editor.Bubble.Panel_.prototype.getContentElement = function() { + return /** @type {Element} */ (this.element.lastChild); +}; diff --git a/closure-library/closure/goog/ui/editor/defaulttoolbar.js b/closure-library/closure/goog/ui/editor/defaulttoolbar.js new file mode 100644 index 0000000000..f8c8378242 --- /dev/null +++ b/closure-library/closure/goog/ui/editor/defaulttoolbar.js @@ -0,0 +1,1080 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Factory functions for creating a default editing toolbar. + * + * @author attila@google.com (Attila Bodis) + * @see ../../demos/editor/editor.html + */ + +goog.provide('goog.ui.editor.ButtonDescriptor'); +goog.provide('goog.ui.editor.DefaultToolbar'); + +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.classlist'); +goog.require('goog.editor.Command'); +goog.require('goog.style'); +goog.require('goog.ui.editor.ToolbarFactory'); +goog.require('goog.ui.editor.messages'); +goog.require('goog.userAgent'); + +// Font menu creation. + + +/** @desc Font menu item caption for the default sans-serif font. */ +goog.ui.editor.DefaultToolbar.MSG_FONT_NORMAL = goog.getMsg('Normal'); + + +/** @desc Font menu item caption for the default serif font. */ +goog.ui.editor.DefaultToolbar.MSG_FONT_NORMAL_SERIF = + goog.getMsg('Normal / serif'); + + +/** + * Common font descriptors for all locales. Each descriptor has the following + * attributes: + *
        + *
      • `caption` - Caption to show in the font menu (e.g. 'Tahoma') + *
      • `value` - Value for the corresponding 'font-family' CSS style + * (e.g. 'Tahoma, Arial, sans-serif') + *
      + * @type {!Array<{caption:string, value:string}>} + * @private + */ +goog.ui.editor.DefaultToolbar.FONTS_ = [ + { + caption: goog.ui.editor.DefaultToolbar.MSG_FONT_NORMAL, + value: 'arial,sans-serif' + }, + { + caption: goog.ui.editor.DefaultToolbar.MSG_FONT_NORMAL_SERIF, + value: 'times new roman,serif' + }, + {caption: 'Courier New', value: 'courier new,monospace'}, + {caption: 'Georgia', value: 'georgia,serif'}, + {caption: 'Trebuchet', value: 'trebuchet ms,sans-serif'}, + {caption: 'Verdana', value: 'verdana,sans-serif'} +]; + + +/** + * Locale-specific font descriptors. The object is a map of locale strings to + * arrays of font descriptors. + * @type {!Object>} + * @private + */ +goog.ui.editor.DefaultToolbar.I18N_FONTS_ = { + 'ja': [ + { + caption: '\uff2d\uff33 \uff30\u30b4\u30b7\u30c3\u30af', + value: 'ms pgothic,sans-serif' + }, + {caption: '\uff2d\uff33 \uff30\u660e\u671d', value: 'ms pmincho,serif'}, { + caption: '\uff2d\uff33 \u30b4\u30b7\u30c3\u30af', + value: 'ms gothic,monospace' + } + ], + 'ko': [ + {caption: '\uad74\ub9bc', value: 'gulim,sans-serif'}, + {caption: '\ubc14\ud0d5', value: 'batang,serif'}, + {caption: '\uad74\ub9bc\uccb4', value: 'gulimche,monospace'} + ], + 'zh-tw': [ + {caption: '\u65b0\u7d30\u660e\u9ad4', value: 'pmingliu,serif'}, + {caption: '\u7d30\u660e\u9ad4', value: 'mingliu,serif'} + ], + 'zh-cn': [ + {caption: '\u5b8b\u4f53', value: 'simsun,serif'}, + {caption: '\u9ed1\u4f53', value: 'simhei,sans-serif'}, + {caption: 'MS Song', value: 'ms song,monospace'} + ] +}; + + +/** + * Default locale for font names. + * @type {string} + * @private + */ +goog.ui.editor.DefaultToolbar.locale_ = 'en-us'; + + +/** + * Sets the locale for the font names. If not set, defaults to 'en-us'. + * Used only for default creation of font names name. Must be set + * before font name menu is created. + * @param {string} locale Locale to use for the toolbar font names. + */ +goog.ui.editor.DefaultToolbar.setLocale = function(locale) { + goog.ui.editor.DefaultToolbar.locale_ = locale; +}; + + +/** + * Initializes the given font menu button by adding default fonts to the menu. + * If goog.ui.editor.DefaultToolbar.setLocale was called to specify a locale + * for which locale-specific default fonts exist, those are added before + * common fonts. + * @param {!goog.ui.Select} button Font menu button. + */ +goog.ui.editor.DefaultToolbar.addDefaultFonts = function(button) { + // Normalize locale to lowercase, with a hyphen (see bug 1036165). + var locale = + goog.ui.editor.DefaultToolbar.locale_.replace(/_/, '-').toLowerCase(); + // Add locale-specific default fonts, if any. + var fontlist = []; + + if (locale in goog.ui.editor.DefaultToolbar.I18N_FONTS_) { + fontlist = goog.ui.editor.DefaultToolbar.I18N_FONTS_[locale]; + } + if (fontlist.length) { + goog.ui.editor.ToolbarFactory.addFonts(button, fontlist); + } + // Add locale-independent default fonts. + goog.ui.editor.ToolbarFactory.addFonts( + button, goog.ui.editor.DefaultToolbar.FONTS_); +}; + + +// Font size menu creation. + + +/** @desc Font size menu item caption for the 'Small' size. */ +goog.ui.editor.DefaultToolbar.MSG_FONT_SIZE_SMALL = goog.getMsg('Small'); + + +/** @desc Font size menu item caption for the 'Normal' size. */ +goog.ui.editor.DefaultToolbar.MSG_FONT_SIZE_NORMAL = goog.getMsg('Normal'); + + +/** @desc Font size menu item caption for the 'Large' size. */ +goog.ui.editor.DefaultToolbar.MSG_FONT_SIZE_LARGE = goog.getMsg('Large'); + + +/** @desc Font size menu item caption for the 'Huge' size. */ +goog.ui.editor.DefaultToolbar.MSG_FONT_SIZE_HUGE = goog.getMsg('Huge'); + + +/** + * Font size descriptors, each with the following attributes: + *
        + *
      • `caption` - Caption to show in the font size menu (e.g. 'Huge') + *
      • `value` - Value for the corresponding HTML font size (e.g. 6) + *
      + * @type {!Array<{caption:string, value:number}>} + * @private + */ +goog.ui.editor.DefaultToolbar.FONT_SIZES_ = [ + {caption: goog.ui.editor.DefaultToolbar.MSG_FONT_SIZE_SMALL, value: 1}, + {caption: goog.ui.editor.DefaultToolbar.MSG_FONT_SIZE_NORMAL, value: 2}, + {caption: goog.ui.editor.DefaultToolbar.MSG_FONT_SIZE_LARGE, value: 4}, + {caption: goog.ui.editor.DefaultToolbar.MSG_FONT_SIZE_HUGE, value: 6} +]; + + +/** + * Initializes the given font size menu button by adding default font sizes to + * it. + * @param {!goog.ui.Select} button Font size menu button. + */ +goog.ui.editor.DefaultToolbar.addDefaultFontSizes = function(button) { + goog.ui.editor.ToolbarFactory.addFontSizes( + button, goog.ui.editor.DefaultToolbar.FONT_SIZES_); +}; + + +// Header format menu creation. + + +/** @desc Caption for "Heading" block format option. */ +goog.ui.editor.DefaultToolbar.MSG_FORMAT_HEADING = goog.getMsg('Heading'); + + +/** @desc Caption for "Subheading" block format option. */ +goog.ui.editor.DefaultToolbar.MSG_FORMAT_SUBHEADING = goog.getMsg('Subheading'); + + +/** @desc Caption for "Minor heading" block format option. */ +goog.ui.editor.DefaultToolbar.MSG_FORMAT_MINOR_HEADING = + goog.getMsg('Minor heading'); + + +/** @desc Caption for "Normal" block format option. */ +goog.ui.editor.DefaultToolbar.MSG_FORMAT_NORMAL = goog.getMsg('Normal'); + + +/** + * Format option descriptors, each with the following attributes: + *
        + *
      • `caption` - Caption to show in the menu (e.g. 'Minor heading') + *
      • `command` - Corresponding {@link goog.dom.TagName} (e.g. + * 'H4') + *
      + * @type {!Array<{caption: string, command: !goog.dom.TagName}>} + * @private + */ +goog.ui.editor.DefaultToolbar.FORMAT_OPTIONS_ = [ + { + caption: goog.ui.editor.DefaultToolbar.MSG_FORMAT_HEADING, + command: goog.dom.TagName.H2 + }, + { + caption: goog.ui.editor.DefaultToolbar.MSG_FORMAT_SUBHEADING, + command: goog.dom.TagName.H3 + }, + { + caption: goog.ui.editor.DefaultToolbar.MSG_FORMAT_MINOR_HEADING, + command: goog.dom.TagName.H4 + }, + { + caption: goog.ui.editor.DefaultToolbar.MSG_FORMAT_NORMAL, + command: goog.dom.TagName.P + } +]; + + +/** + * Initializes the given "Format block" menu button by adding default format + * options to the menu. + * @param {!goog.ui.Select} button "Format block" menu button. + */ +goog.ui.editor.DefaultToolbar.addDefaultFormatOptions = function(button) { + goog.ui.editor.ToolbarFactory.addFormatOptions( + button, goog.ui.editor.DefaultToolbar.FORMAT_OPTIONS_); +}; + + +/** + * Creates a {@link goog.ui.Toolbar} containing a default set of editor + * toolbar buttons, and renders it into the given parent element. + * @param {!Element} elem Toolbar parent element. + * @param {boolean=} opt_isRightToLeft Whether the editor chrome is + * right-to-left; defaults to the directionality of the toolbar parent + * element. + * @return {!goog.ui.Toolbar} Default editor toolbar, rendered into the given + * parent element. + * @see goog.ui.editor.DefaultToolbar.DEFAULT_BUTTONS + */ +goog.ui.editor.DefaultToolbar.makeDefaultToolbar = function( + elem, opt_isRightToLeft) { + var isRightToLeft = opt_isRightToLeft || goog.style.isRightToLeft(elem); + var buttons = isRightToLeft ? + goog.ui.editor.DefaultToolbar.DEFAULT_BUTTONS_RTL : + goog.ui.editor.DefaultToolbar.DEFAULT_BUTTONS; + return goog.ui.editor.DefaultToolbar.makeToolbar( + buttons, elem, opt_isRightToLeft); +}; + + +/** + * Creates a {@link goog.ui.Toolbar} containing the specified set of + * toolbar buttons, and renders it into the given parent element. Each + * item in the `items` array must either be a + * {@link goog.editor.Command} (to create a built-in button) or a subclass + * of {@link goog.ui.Control} (to create a custom control). + * @param {!Array} items Toolbar items; each must + * be a {@link goog.editor.Command} or a {@link goog.ui.Control}. + * @param {!Element} elem Toolbar parent element. + * @param {boolean=} opt_isRightToLeft Whether the editor chrome is + * right-to-left; defaults to the directionality of the toolbar parent + * element. + * @return {!goog.ui.Toolbar} Editor toolbar, rendered into the given parent + * element. + */ +goog.ui.editor.DefaultToolbar.makeToolbar = function( + items, elem, opt_isRightToLeft) { + var domHelper = goog.dom.getDomHelper(elem); + var controls = []; + + for (var i = 0, button; button = items[i]; i++) { + if (goog.isString(button)) { + button = goog.ui.editor.DefaultToolbar.makeBuiltInToolbarButton( + button, domHelper); + } + if (button) { + controls.push(button); + } + } + + return goog.ui.editor.ToolbarFactory.makeToolbar( + controls, elem, opt_isRightToLeft); +}; + + +/** + * Creates an instance of a subclass of {@link goog.ui.Button} for the given + * {@link goog.editor.Command}, or null if no built-in button exists for the + * command. Note that this function is only intended to create built-in + * buttons; please don't try to hack it! + * @param {string} command Editor command ID. + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM + * creation; defaults to the current document if unspecified. + * @return {goog.ui.Button} Toolbar button (null if no built-in button exists + * for the command). + */ +goog.ui.editor.DefaultToolbar.makeBuiltInToolbarButton = function( + command, opt_domHelper) { + var button = null; + var descriptor = goog.ui.editor.DefaultToolbar.buttons_[command]; + if (descriptor) { + // Default the factory method to makeToggleButton, since most built-in + // toolbar buttons are toggle buttons. See also + // goog.ui.editor.DefaultToolbar.button_list_. + /** @type {!Function} */ + var factory = + descriptor.factory || goog.ui.editor.ToolbarFactory.makeToggleButton; + var id = descriptor.command; + var tooltip = descriptor.tooltip; + var caption = descriptor.caption; + var classNames = descriptor.classes; + // Default the DOM helper to the one for the current document. + var domHelper = opt_domHelper || goog.dom.getDomHelper(); + // Instantiate the button based on the descriptor. + button = factory(id, tooltip, caption, classNames, null, domHelper); + // If this button's state should be queried when updating the toolbar, + // set the button object's queryable property to true. + if (descriptor.queryable) { + button.queryable = true; + } + } + return button; +}; + + +/** + * A set of built-in buttons to display in the default editor toolbar. + * @type {!Array} + */ +goog.ui.editor.DefaultToolbar.DEFAULT_BUTTONS = [ + goog.editor.Command.IMAGE, goog.editor.Command.LINK, goog.editor.Command.BOLD, + goog.editor.Command.ITALIC, goog.editor.Command.UNORDERED_LIST, + goog.editor.Command.FONT_COLOR, goog.editor.Command.FONT_FACE, + goog.editor.Command.FONT_SIZE, goog.editor.Command.JUSTIFY_LEFT, + goog.editor.Command.JUSTIFY_CENTER, goog.editor.Command.JUSTIFY_RIGHT, + goog.editor.Command.EDIT_HTML +]; + + +/** + * A set of built-in buttons to display in the default editor toolbar when + * the editor chrome is right-to-left (BiDi mode only). + * @type {!Array} + */ +goog.ui.editor.DefaultToolbar.DEFAULT_BUTTONS_RTL = [ + goog.editor.Command.IMAGE, goog.editor.Command.LINK, goog.editor.Command.BOLD, + goog.editor.Command.ITALIC, goog.editor.Command.UNORDERED_LIST, + goog.editor.Command.FONT_COLOR, goog.editor.Command.FONT_FACE, + goog.editor.Command.FONT_SIZE, goog.editor.Command.JUSTIFY_RIGHT, + goog.editor.Command.JUSTIFY_CENTER, goog.editor.Command.JUSTIFY_LEFT, + goog.editor.Command.DIR_RTL, goog.editor.Command.DIR_LTR, + goog.editor.Command.EDIT_HTML +]; + + +/** + * Creates a toolbar button with the given ID, tooltip, and caption. Applies + * any custom CSS class names to the button's caption element. This button + * is designed to be used as the RTL button. + * @param {string} id Button ID; must equal a {@link goog.editor.Command} for + * built-in buttons, anything else for custom buttons. + * @param {string} tooltip Tooltip to be shown on hover. + * @param {goog.ui.ControlContent} caption Button caption. + * @param {string=} opt_classNames CSS class name(s) to apply to the caption + * element. + * @param {goog.ui.ButtonRenderer=} opt_renderer Button renderer; defaults to + * {@link goog.ui.ToolbarButtonRenderer} if unspecified. + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM + * creation; defaults to the current document if unspecified. + * @return {!goog.ui.Button} A toolbar button. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.editor.DefaultToolbar.rtlButtonFactory_ = function( + id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper) { + var button = goog.ui.editor.ToolbarFactory.makeToggleButton( + id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper); + button.updateFromValue = function(value) { + // Enable/disable right-to-left text editing mode in the toolbar. + var isRtl = !!value; + // Enable/disable a marker class on the toolbar's root element; the rest is + // done using CSS scoping in editortoolbar.css. This changes + // direction-senitive toolbar icons (like indent/outdent) + goog.dom.classlist.enable( + goog.asserts.assert(button.getParent().getElement()), + goog.getCssName('tr-rtl-mode'), isRtl); + button.setChecked(isRtl); + }; + return button; +}; + + +/** + * Creates a toolbar button with the given ID, tooltip, and caption. Applies + * any custom CSS class names to the button's caption element. Designed to + * be used to create undo and redo buttons. + * @param {string} id Button ID; must equal a {@link goog.editor.Command} for + * built-in buttons, anything else for custom buttons. + * @param {string} tooltip Tooltip to be shown on hover. + * @param {goog.ui.ControlContent} caption Button caption. + * @param {string=} opt_classNames CSS class name(s) to apply to the caption + * element. + * @param {goog.ui.ButtonRenderer=} opt_renderer Button renderer; defaults to + * {@link goog.ui.ToolbarButtonRenderer} if unspecified. + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM + * creation; defaults to the current document if unspecified. + * @return {!goog.ui.Button} A toolbar button. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.editor.DefaultToolbar.undoRedoButtonFactory_ = function( + id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper) { + var button = goog.ui.editor.ToolbarFactory.makeButton( + id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper); + button.updateFromValue = function(value) { + button.setEnabled(value); + }; + return button; +}; + + +/** + * Creates a toolbar button with the given ID, tooltip, and caption. Applies + * any custom CSS class names to the button's caption element. Used to create + * a font face button, filled with default fonts. + * @param {string} id Button ID; must equal a {@link goog.editor.Command} for + * built-in buttons, anything else for custom buttons. + * @param {string} tooltip Tooltip to be shown on hover. + * @param {goog.ui.ControlContent} caption Button caption. + * @param {string=} opt_classNames CSS class name(s) to apply to the caption + * element. + * @param {goog.ui.MenuButtonRenderer=} opt_renderer Button renderer; defaults + * to {@link goog.ui.ToolbarMenuButtonRenderer} if unspecified. + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM + * creation; defaults to the current document if unspecified. + * @return {!goog.ui.Button} A toolbar button. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.editor.DefaultToolbar.fontFaceFactory_ = function( + id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper) { + var button = goog.ui.editor.ToolbarFactory.makeSelectButton( + id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper); + goog.ui.editor.DefaultToolbar.addDefaultFonts(button); + button.setDefaultCaption(goog.ui.editor.DefaultToolbar.MSG_FONT_NORMAL); + // Font options don't have keyboard accelerators. + goog.dom.classlist.add( + goog.asserts.assert(button.getMenu().getContentElement()), + goog.getCssName('goog-menu-noaccel')); + + // How to update this button's state. + button.updateFromValue = function(value) { + // Normalize value to null or a non-empty string (sometimes we get + // the empty string, sometimes we get false...), extract the substring + // up to the first comma to get the primary font name, and normalize + // to lowercase. This allows us to map a font spec like "Arial, + // Helvetica, sans-serif" to a font menu item. + // TODO (attila): Try to make this more robust. + var item = null; + if (value && value.length > 0) { + item = /** @type {goog.ui.MenuItem} */ (button.getMenu().getChild( + goog.ui.editor.ToolbarFactory.getPrimaryFont(value))); + } + var selectedItem = button.getSelectedItem(); + if (item != selectedItem) { + button.setSelectedItem(item); + } + }; + return button; +}; + + +/** + * Creates a toolbar button with the given ID, tooltip, and caption. Applies + * any custom CSS class names to the button's caption element. Use to create a + * font size button, filled with default font sizes. + * @param {string} id Button ID; must equal a {@link goog.editor.Command} for + * built-in buttons, anything else for custom buttons. + * @param {string} tooltip Tooltip to be shown on hover. + * @param {goog.ui.ControlContent} caption Button caption. + * @param {string=} opt_classNames CSS class name(s) to apply to the caption + * element. + * @param {goog.ui.MenuButtonRenderer=} opt_renderer Button renderer; defaults + * to {@link goog.ui.ToolbarMebuButtonRenderer} if unspecified. + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM + * creation; defaults to the current document if unspecified. + * @return {!goog.ui.Button} A toolbar button. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.editor.DefaultToolbar.fontSizeFactory_ = function( + id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper) { + var button = goog.ui.editor.ToolbarFactory.makeSelectButton( + id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper); + goog.ui.editor.DefaultToolbar.addDefaultFontSizes(button); + button.setDefaultCaption(goog.ui.editor.DefaultToolbar.MSG_FONT_SIZE_NORMAL); + // Font size options don't have keyboard accelerators. + goog.dom.classlist.add( + goog.asserts.assert(button.getMenu().getContentElement()), + goog.getCssName('goog-menu-noaccel')); + // How to update this button's state. + button.updateFromValue = function(value) { + // Webkit pre-534.7 returns a string like '32px' instead of the equivalent + // integer, so normalize that first. + // NOTE(user): Gecko returns "6" so can't just normalize all + // strings, only ones ending in "px". + if (goog.isString(value) && goog.style.getLengthUnits(value) == 'px') { + value = goog.ui.editor.ToolbarFactory.getLegacySizeFromPx( + parseInt(value, 10)); + } + // Normalize value to null or a positive integer (sometimes we get + // the empty string, sometimes we get false, or -1 if the above + // normalization didn't match to a particular 0-7 size) + value = value > 0 ? value : null; + if (value != button.getValue()) { + button.setValue(value); + } + }; + return button; +}; + + +/** + * Function to update the state of a color menu button. + * @param {goog.ui.ToolbarColorMenuButton} button The button to which the + * color menu is attached. + * @param {number} color Color value to update to. + * @private + */ +goog.ui.editor.DefaultToolbar.colorUpdateFromValue_ = function(button, color) { + var value = color; + + try { + if (goog.userAgent.IE) { + // IE returns a number that, converted to hex, is a BGR color. + // Convert from decimal to BGR to RGB. + var hex = '000000' + value.toString(16); + var bgr = hex.substr(hex.length - 6, 6); + value = + '#' + bgr.substring(4, 6) + bgr.substring(2, 4) + bgr.substring(0, 2); + } + if (value != button.getValue()) { + button.setValue(/** @type {string} */ (value)); + } + } catch (ex) { + // TODO(attila): Find out when/why this happens. + } +}; + + +/** + * Creates a toolbar button with the given ID, tooltip, and caption. Applies + * any custom CSS class names to the button's caption element. Use to create + * a font color button. + * @param {string} id Button ID; must equal a {@link goog.editor.Command} for + * built-in buttons, anything else for custom buttons. + * @param {string} tooltip Tooltip to be shown on hover. + * @param {goog.ui.ControlContent} caption Button caption. + * @param {string=} opt_classNames CSS class name(s) to apply to the caption + * element. + * @param {goog.ui.ColorMenuButtonRenderer=} opt_renderer Button renderer; + * defaults to {@link goog.ui.ToolbarColorMenuButtonRenderer} if + * unspecified. + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM + * creation; defaults to the current document if unspecified. + * @return {!goog.ui.Button} A toolbar button. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.editor.DefaultToolbar.fontColorFactory_ = function( + id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper) { + var button = goog.ui.editor.ToolbarFactory.makeColorMenuButton( + id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper); + // Initialize default foreground color. + button.setSelectedColor('#000'); + button.updateFromValue = goog.partial( + goog.ui.editor.DefaultToolbar.colorUpdateFromValue_, + /** @type {!goog.ui.ToolbarColorMenuButton} */ (button)); + return button; +}; + + +/** + * Creates a toolbar button with the given ID, tooltip, and caption. Applies + * any custom CSS class names to the button's caption element. Use to create + * a font background color button. + * @param {string} id Button ID; must equal a {@link goog.editor.Command} for + * built-in buttons, anything else for custom buttons. + * @param {string} tooltip Tooltip to be shown on hover. + * @param {goog.ui.ControlContent} caption Button caption. + * @param {string=} opt_classNames CSS class name(s) to apply to the caption + * element. + * @param {goog.ui.ColorMenuButtonRenderer=} opt_renderer Button renderer; + * defaults to {@link goog.ui.ToolbarColorMenuButtonRenderer} if + * unspecified. + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM + * creation; defaults to the current document if unspecified. + * @return {!goog.ui.Button} A toolbar button. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.editor.DefaultToolbar.backgroundColorFactory_ = function( + id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper) { + var button = goog.ui.editor.ToolbarFactory.makeColorMenuButton( + id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper); + // Initialize default background color. + button.setSelectedColor('#FFF'); + button.updateFromValue = goog.partial( + goog.ui.editor.DefaultToolbar.colorUpdateFromValue_, + /** @type {!goog.ui.ToolbarColorMenuButton} */ (button)); + return button; +}; + + +/** + * Creates a toolbar button with the given ID, tooltip, and caption. Applies + * any custom CSS class names to the button's caption element. Use to create + * the format menu, prefilled with default formats. + * @param {string} id Button ID; must equal a {@link goog.editor.Command} for + * built-in buttons, anything else for custom buttons. + * @param {string} tooltip Tooltip to be shown on hover. + * @param {goog.ui.ControlContent} caption Button caption. + * @param {string=} opt_classNames CSS class name(s) to apply to the caption + * element. + * @param {goog.ui.MenuButtonRenderer=} opt_renderer Button renderer; + * defaults to + * {@link goog.ui.ToolbarMenuButtonRenderer} if unspecified. + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM + * creation; defaults to the current document if unspecified. + * @return {!goog.ui.Button} A toolbar button. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.editor.DefaultToolbar.formatBlockFactory_ = function( + id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper) { + var button = goog.ui.editor.ToolbarFactory.makeSelectButton( + id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper); + goog.ui.editor.DefaultToolbar.addDefaultFormatOptions(button); + button.setDefaultCaption(goog.ui.editor.DefaultToolbar.MSG_FORMAT_NORMAL); + // Format options don't have keyboard accelerators. + goog.dom.classlist.add( + goog.asserts.assert(button.getMenu().getContentElement()), + goog.getCssName('goog-menu-noaccel')); + // How to update this button. + button.updateFromValue = function(value) { + // Normalize value to null or a nonempty string (sometimes we get + // the empty string, sometimes we get false...) + value = value && value.length > 0 ? value : null; + if (value != button.getValue()) { + button.setValue(value); + } + }; + return button; +}; + + +// Messages used for tooltips and captions. + + +/** @desc Format menu tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_FORMAT_BLOCK_TITLE = goog.getMsg('Format'); + + +/** @desc Format menu caption. */ +goog.ui.editor.DefaultToolbar.MSG_FORMAT_BLOCK_CAPTION = goog.getMsg('Format'); + + +/** @desc Undo button tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_UNDO_TITLE = goog.getMsg('Undo'); + + +/** @desc Redo button tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_REDO_TITLE = goog.getMsg('Redo'); + + +/** @desc Font menu tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_FONT_FACE_TITLE = goog.getMsg('Font'); + + +/** @desc Font size menu tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_FONT_SIZE_TITLE = goog.getMsg('Font size'); + + +/** @desc Text foreground color menu tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_FONT_COLOR_TITLE = goog.getMsg('Text color'); + + +/** @desc Bold button tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_BOLD_TITLE = goog.getMsg('Bold'); + + +/** @desc Italic button tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_ITALIC_TITLE = goog.getMsg('Italic'); + + +/** @desc Underline button tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_UNDERLINE_TITLE = goog.getMsg('Underline'); + + +/** @desc Text background color menu tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_BACKGROUND_COLOR_TITLE = + goog.getMsg('Text background color'); + + +/** @desc Link button tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_LINK_TITLE = + goog.getMsg('Add or remove link'); + + +/** @desc Numbered list button tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_ORDERED_LIST_TITLE = + goog.getMsg('Numbered list'); + + +/** @desc Bullet list button tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_UNORDERED_LIST_TITLE = + goog.getMsg('Bullet list'); + + +/** @desc Outdent button tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_OUTDENT_TITLE = + goog.getMsg('Decrease indent'); + + +/** @desc Indent button tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_INDENT_TITLE = goog.getMsg('Increase indent'); + + +/** @desc Align left button tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_ALIGN_LEFT_TITLE = goog.getMsg('Align left'); + + +/** @desc Align center button tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_ALIGN_CENTER_TITLE = + goog.getMsg('Align center'); + + +/** @desc Align right button tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_ALIGN_RIGHT_TITLE = + goog.getMsg('Align right'); + + +/** @desc Justify button tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_JUSTIFY_TITLE = goog.getMsg('Justify'); + + +/** @desc Remove formatting button tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_REMOVE_FORMAT_TITLE = + goog.getMsg('Remove formatting'); + + +/** @desc Insert image button tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_IMAGE_TITLE = goog.getMsg('Insert image'); + + +/** @desc Strike through button tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_STRIKE_THROUGH_TITLE = + goog.getMsg('Strikethrough'); + + +/** @desc Left-to-right button tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_DIR_LTR_TITLE = goog.getMsg('Left-to-right'); + + +/** @desc Right-to-left button tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_DIR_RTL_TITLE = goog.getMsg('Right-to-left'); + + +/** @desc Blockquote button tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_BLOCKQUOTE_TITLE = goog.getMsg('Quote'); + + +/** @desc Edit HTML button tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_EDIT_HTML_TITLE = + goog.getMsg('Edit HTML source'); + + +/** @desc Subscript button tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_SUBSCRIPT = goog.getMsg('Subscript'); + + +/** @desc Superscript button tooltip. */ +goog.ui.editor.DefaultToolbar.MSG_SUPERSCRIPT = goog.getMsg('Superscript'); + + +/** @desc Edit HTML button caption. */ +goog.ui.editor.DefaultToolbar.MSG_EDIT_HTML_CAPTION = goog.getMsg('Edit HTML'); + + +/** + * Map of `goog.editor.Command`s to toolbar button descriptor objects, + * each of which has the following attributes: + *
        + *
      • `command` - The command corresponding to the + * button (mandatory) + *
      • `tooltip` - Tooltip text (optional); if unspecified, the button + * has no hover text + *
      • `caption` - Caption to display on the button (optional); if + * unspecified, the button has no text caption + *
      • `classes` - CSS class name(s) to be applied to the button's + * element when rendered (optional); if unspecified, defaults to + * 'tr-icon' + * plus 'tr-' followed by the command ID, but without any leading '+' + * character (e.g. if the command ID is '+undo', then `classes` + * defaults to 'tr-icon tr-undo') + *
      • `factory` - factory function used to create the button, which + * must accept `id`, `tooltip`, `caption`, and + * `classes` as arguments, and must return an instance of + * {@link goog.ui.Button} or an appropriate subclass (optional); if + * unspecified, defaults to + * {@link goog.ui.editor.DefaultToolbar.makeToggleButton}, + * since most built-in toolbar buttons are toggle buttons + *
      • (@code queryable} - Whether the button's state should be queried + * when updating the toolbar (optional). + *
      + * Note that this object is only used for creating toolbar buttons for + * built-in editor commands; custom buttons aren't listed here. Please don't + * try to hack this! + * @private {!Object}. + */ +goog.ui.editor.DefaultToolbar.buttons_ = {}; + + +/** + * @typedef {{ + * command: string, + * tooltip: (undefined|string), + * caption: (undefined|goog.ui.ControlContent), + * classes: (undefined|string), + * factory: (undefined|!Function), + * queryable:(undefined|boolean)}} + */ +goog.ui.editor.ButtonDescriptor; + + +/** + * Built-in toolbar button descriptors. See + * {@link goog.ui.editor.DefaultToolbar.buttons_} for details on button + * descriptor objects. This array is processed at JS parse time; each item is + * inserted into {@link goog.ui.editor.DefaultToolbar.buttons_}, and the array + * itself is deleted and (hopefully) garbage-collected. + * @private {Array} + */ +goog.ui.editor.DefaultToolbar.button_list_ = [ + { + command: goog.editor.Command.UNDO, + tooltip: goog.ui.editor.DefaultToolbar.MSG_UNDO_TITLE, + classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-undo'), + factory: goog.ui.editor.DefaultToolbar.undoRedoButtonFactory_, + queryable: true + }, + { + command: goog.editor.Command.REDO, + tooltip: goog.ui.editor.DefaultToolbar.MSG_REDO_TITLE, + classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-redo'), + factory: goog.ui.editor.DefaultToolbar.undoRedoButtonFactory_, + queryable: true + }, + { + command: goog.editor.Command.FONT_FACE, + tooltip: goog.ui.editor.DefaultToolbar.MSG_FONT_FACE_TITLE, + classes: goog.getCssName('tr-fontName'), + factory: goog.ui.editor.DefaultToolbar.fontFaceFactory_, + queryable: true + }, + { + command: goog.editor.Command.FONT_SIZE, + tooltip: goog.ui.editor.DefaultToolbar.MSG_FONT_SIZE_TITLE, + classes: goog.getCssName('tr-fontSize'), + factory: goog.ui.editor.DefaultToolbar.fontSizeFactory_, + queryable: true + }, + { + command: goog.editor.Command.BOLD, + tooltip: goog.ui.editor.DefaultToolbar.MSG_BOLD_TITLE, + classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-bold'), + queryable: true + }, + { + command: goog.editor.Command.ITALIC, + tooltip: goog.ui.editor.DefaultToolbar.MSG_ITALIC_TITLE, + classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-italic'), + queryable: true + }, + { + command: goog.editor.Command.UNDERLINE, + tooltip: goog.ui.editor.DefaultToolbar.MSG_UNDERLINE_TITLE, + classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-underline'), + queryable: true + }, + { + command: goog.editor.Command.FONT_COLOR, + tooltip: goog.ui.editor.DefaultToolbar.MSG_FONT_COLOR_TITLE, + classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-foreColor'), + factory: goog.ui.editor.DefaultToolbar.fontColorFactory_, + queryable: true + }, + { + command: goog.editor.Command.BACKGROUND_COLOR, + tooltip: goog.ui.editor.DefaultToolbar.MSG_BACKGROUND_COLOR_TITLE, + classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-backColor'), + factory: goog.ui.editor.DefaultToolbar.backgroundColorFactory_, + queryable: true + }, + { + command: goog.editor.Command.LINK, + tooltip: goog.ui.editor.DefaultToolbar.MSG_LINK_TITLE, + caption: goog.ui.editor.messages.MSG_LINK_CAPTION, + classes: goog.getCssName('tr-link'), + queryable: true + }, + { + command: goog.editor.Command.ORDERED_LIST, + tooltip: goog.ui.editor.DefaultToolbar.MSG_ORDERED_LIST_TITLE, + classes: goog.getCssName('tr-icon') + ' ' + + goog.getCssName('tr-insertOrderedList'), + queryable: true + }, + { + command: goog.editor.Command.UNORDERED_LIST, + tooltip: goog.ui.editor.DefaultToolbar.MSG_UNORDERED_LIST_TITLE, + classes: goog.getCssName('tr-icon') + ' ' + + goog.getCssName('tr-insertUnorderedList'), + queryable: true + }, + { + command: goog.editor.Command.OUTDENT, + tooltip: goog.ui.editor.DefaultToolbar.MSG_OUTDENT_TITLE, + classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-outdent'), + factory: goog.ui.editor.ToolbarFactory.makeButton + }, + { + command: goog.editor.Command.INDENT, + tooltip: goog.ui.editor.DefaultToolbar.MSG_INDENT_TITLE, + classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-indent'), + factory: goog.ui.editor.ToolbarFactory.makeButton + }, + { + command: goog.editor.Command.JUSTIFY_LEFT, + tooltip: goog.ui.editor.DefaultToolbar.MSG_ALIGN_LEFT_TITLE, + classes: + goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-justifyLeft'), + queryable: true + }, + { + command: goog.editor.Command.JUSTIFY_CENTER, + tooltip: goog.ui.editor.DefaultToolbar.MSG_ALIGN_CENTER_TITLE, + classes: + goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-justifyCenter'), + queryable: true + }, + { + command: goog.editor.Command.JUSTIFY_RIGHT, + tooltip: goog.ui.editor.DefaultToolbar.MSG_ALIGN_RIGHT_TITLE, + classes: + goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-justifyRight'), + queryable: true + }, + { + command: goog.editor.Command.JUSTIFY_FULL, + tooltip: goog.ui.editor.DefaultToolbar.MSG_JUSTIFY_TITLE, + classes: + goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-justifyFull'), + queryable: true + }, + { + command: goog.editor.Command.REMOVE_FORMAT, + tooltip: goog.ui.editor.DefaultToolbar.MSG_REMOVE_FORMAT_TITLE, + classes: + goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-removeFormat'), + factory: goog.ui.editor.ToolbarFactory.makeButton + }, + { + command: goog.editor.Command.IMAGE, + tooltip: goog.ui.editor.DefaultToolbar.MSG_IMAGE_TITLE, + classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-image'), + factory: goog.ui.editor.ToolbarFactory.makeButton + }, + { + command: goog.editor.Command.STRIKE_THROUGH, + tooltip: goog.ui.editor.DefaultToolbar.MSG_STRIKE_THROUGH_TITLE, + classes: + goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-strikeThrough'), + queryable: true + }, + { + command: goog.editor.Command.SUBSCRIPT, + tooltip: goog.ui.editor.DefaultToolbar.MSG_SUBSCRIPT, + classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-subscript'), + queryable: true + }, + { + command: goog.editor.Command.SUPERSCRIPT, + tooltip: goog.ui.editor.DefaultToolbar.MSG_SUPERSCRIPT, + classes: + goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-superscript'), + queryable: true + }, + { + command: goog.editor.Command.DIR_LTR, + tooltip: goog.ui.editor.DefaultToolbar.MSG_DIR_LTR_TITLE, + classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-ltr'), + queryable: true + }, + { + command: goog.editor.Command.DIR_RTL, + tooltip: goog.ui.editor.DefaultToolbar.MSG_DIR_RTL_TITLE, + classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-rtl'), + factory: goog.ui.editor.DefaultToolbar.rtlButtonFactory_, + queryable: true + }, + { + command: goog.editor.Command.BLOCKQUOTE, + tooltip: goog.ui.editor.DefaultToolbar.MSG_BLOCKQUOTE_TITLE, + classes: + goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-BLOCKQUOTE'), + queryable: true + }, + { + command: goog.editor.Command.FORMAT_BLOCK, + tooltip: goog.ui.editor.DefaultToolbar.MSG_FORMAT_BLOCK_TITLE, + caption: goog.ui.editor.DefaultToolbar.MSG_FORMAT_BLOCK_CAPTION, + classes: goog.getCssName('tr-formatBlock'), + factory: goog.ui.editor.DefaultToolbar.formatBlockFactory_, + queryable: true + }, + { + command: goog.editor.Command.EDIT_HTML, + tooltip: goog.ui.editor.DefaultToolbar.MSG_EDIT_HTML_TITLE, + caption: goog.ui.editor.DefaultToolbar.MSG_EDIT_HTML_CAPTION, + classes: goog.getCssName('tr-editHtml'), + factory: goog.ui.editor.ToolbarFactory.makeButton + } +]; + + +(function() { +// Create the goog.ui.editor.DefaultToolbar.buttons_ map from +// goog.ui.editor.DefaultToolbar.button_list_. +for (var i = 0, button; button = goog.ui.editor.DefaultToolbar.button_list_[i]; + i++) { + goog.ui.editor.DefaultToolbar.buttons_[button.command] = button; +} + +// goog.ui.editor.DefaultToolbar.button_list_ is no longer needed +// once the map is ready. +goog.ui.editor.DefaultToolbar.button_list_ = null; +})(); diff --git a/closure-library/closure/goog/ui/editor/linkdialog.js b/closure-library/closure/goog/ui/editor/linkdialog.js new file mode 100644 index 0000000000..14d43724fb --- /dev/null +++ b/closure-library/closure/goog/ui/editor/linkdialog.js @@ -0,0 +1,1113 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A dialog for editing/creating a link. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.ui.editor.LinkDialog'); +goog.provide('goog.ui.editor.LinkDialog.BeforeTestLinkEvent'); +goog.provide('goog.ui.editor.LinkDialog.EventType'); +goog.provide('goog.ui.editor.LinkDialog.OkEvent'); + +goog.require('goog.a11y.aria'); +goog.require('goog.a11y.aria.State'); +goog.require('goog.dom'); +goog.require('goog.dom.InputType'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.safe'); +goog.require('goog.editor.BrowserFeature'); +goog.require('goog.editor.Link'); +goog.require('goog.editor.focus'); +goog.require('goog.editor.node'); +goog.require('goog.events.Event'); +goog.require('goog.events.EventHandler'); +goog.require('goog.events.InputHandler'); +goog.require('goog.html.SafeHtml'); +goog.require('goog.html.SafeHtmlFormatter'); +goog.require('goog.string'); +goog.require('goog.string.Unicode'); +goog.require('goog.style'); +goog.require('goog.ui.Button'); +goog.require('goog.ui.Component'); +goog.require('goog.ui.LinkButtonRenderer'); +goog.require('goog.ui.editor.AbstractDialog'); +goog.require('goog.ui.editor.TabPane'); +goog.require('goog.ui.editor.messages'); +goog.require('goog.userAgent'); +goog.require('goog.window'); + + + +/** + * A type of goog.ui.editor.AbstractDialog for editing/creating a link. + * @param {goog.dom.DomHelper} domHelper DomHelper to be used to create the + * dialog's dom structure. + * @param {goog.editor.Link} link The target link. + * @constructor + * @extends {goog.ui.editor.AbstractDialog} + * @final + */ +goog.ui.editor.LinkDialog = function(domHelper, link) { + goog.ui.editor.LinkDialog.base(this, 'constructor', domHelper); + + /** + * The link being modified by this dialog. + * @type {goog.editor.Link} + * @private + */ + this.targetLink_ = link; + + /** + * The event handler for this dialog. + * @type {goog.events.EventHandler} + * @private + */ + this.eventHandler_ = new goog.events.EventHandler(this); + this.registerDisposable(this.eventHandler_); + + /** + * Optional warning to show about email addresses. + * @type {goog.html.SafeHtml} + * @private + */ + this.emailWarning_ = null; + + /** + * Whether to show a checkbox where the user can choose to have the link open + * in a new window. + * @type {boolean} + * @private + */ + this.showOpenLinkInNewWindow_ = false; + + /** + * Whether to focus the text to display input instead of the url input if the + * text to display input is empty when the dialog opens. + * @type {boolean} + * @private + */ + this.focusTextToDisplayOnOpenIfEmpty_ = false; + + /** + * Whether the "open link in new window" checkbox should be checked when the + * dialog is shown, and also whether it was checked last time the dialog was + * closed. + * @type {boolean} + * @private + */ + this.isOpenLinkInNewWindowChecked_ = false; + + /** + * Whether to show a checkbox where the user can choose to have 'rel=nofollow' + * attribute added to the link. + * @type {boolean} + * @private + */ + this.showRelNoFollow_ = false; + + /** + * InputHandler object to listen for changes in the url input field. + * @type {goog.events.InputHandler} + * @private + */ + this.urlInputHandler_ = null; + + /** + * InputHandler object to listen for changes in the email input field. + * @type {goog.events.InputHandler} + * @private + */ + this.emailInputHandler_ = null; + + /** + * InputHandler object to listen for changes in the text to display input + * field. + * @type {goog.events.InputHandler} + * @private + */ + this.textInputHandler_ = null; + + /** + * The tab bar where the url and email tabs are. + * @type {goog.ui.editor.TabPane} + * @private + */ + this.tabPane_ = null; + + /** + * The div element holding the link's display text input. + * @type {HTMLDivElement} + * @private + */ + this.textToDisplayDiv_ = null; + + /** + * The input element holding the link's display text. + * @type {HTMLInputElement} + * @private + */ + this.textToDisplayInput_ = null; + + /** + * Whether or not the feature of automatically generating the display text is + * enabled. + * @type {boolean} + * @private + */ + this.autogenFeatureEnabled_ = true; + + /** + * Whether or not we should automatically generate the display text. + * @type {boolean} + * @private + */ + this.autogenerateTextToDisplay_ = false; + + /** + * Whether or not automatic generation of the display text is disabled. + * @type {boolean} + * @private + */ + this.disableAutogen_ = false; + + /** + * The input element (checkbox) to indicate that the link should open in a new + * window. + * @type {HTMLInputElement} + * @private + */ + this.openInNewWindowCheckbox_ = null; + + /** + * The input element (checkbox) to indicate that the link should have + * 'rel=nofollow' attribute. + * @type {HTMLInputElement} + * @private + */ + this.relNoFollowCheckbox_ = null; + + /** + * Whether to stop leaking the page's url via the referrer header when the + * "test this link" link is clicked. + * @type {boolean} + * @private + */ + this.stopReferrerLeaks_ = false; +}; +goog.inherits(goog.ui.editor.LinkDialog, goog.ui.editor.AbstractDialog); + + +/** + * Events specific to the link dialog. + * @enum {string} + */ +goog.ui.editor.LinkDialog.EventType = { + BEFORE_TEST_LINK: 'beforetestlink' +}; + + + +/** + * OK event object for the link dialog. + * @param {string} linkText Text the user chose to display for the link. + * @param {string} linkUrl Url the user chose for the link to point to. + * @param {boolean} openInNewWindow Whether the link should open in a new window + * when clicked. + * @param {boolean} noFollow Whether the link should have 'rel=nofollow' + * attribute. + * @constructor + * @extends {goog.events.Event} + * @final + */ +goog.ui.editor.LinkDialog.OkEvent = function( + linkText, linkUrl, openInNewWindow, noFollow) { + goog.ui.editor.LinkDialog.OkEvent.base( + this, 'constructor', goog.ui.editor.AbstractDialog.EventType.OK); + + /** + * The text of the link edited in the dialog. + * @type {string} + */ + this.linkText = linkText; + + /** + * The url of the link edited in the dialog. + * @type {string} + */ + this.linkUrl = linkUrl; + + /** + * Whether the link should open in a new window when clicked. + * @type {boolean} + */ + this.openInNewWindow = openInNewWindow; + + /** + * Whether the link should have 'rel=nofollow' attribute. + * @type {boolean} + */ + this.noFollow = noFollow; +}; +goog.inherits(goog.ui.editor.LinkDialog.OkEvent, goog.events.Event); + + + +/** + * Event fired before testing a link by opening it in another window. + * Calling preventDefault will stop the link from being opened. + * @param {string} url Url of the link being tested. + * @constructor + * @extends {goog.events.Event} + * @final + */ +goog.ui.editor.LinkDialog.BeforeTestLinkEvent = function(url) { + goog.ui.editor.LinkDialog.BeforeTestLinkEvent.base( + this, 'constructor', + goog.ui.editor.LinkDialog.EventType.BEFORE_TEST_LINK); + + /** + * The url of the link being tested. + * @type {string} + */ + this.url = url; +}; +goog.inherits(goog.ui.editor.LinkDialog.BeforeTestLinkEvent, goog.events.Event); + + +/** + * Sets the warning message to show to users about including email addresses on + * public web pages. + * @param {!goog.html.SafeHtml} emailWarning Warning message to show users about + * including email addresses on the web. + */ +goog.ui.editor.LinkDialog.prototype.setEmailWarning = function(emailWarning) { + this.emailWarning_ = emailWarning; +}; + + +/** + * Tells the dialog to show a checkbox where the user can choose to have the + * link open in a new window. + * @param {boolean} startChecked Whether to check the checkbox the first + * time the dialog is shown. Subesquent times the checkbox will remember its + * previous state. + */ +goog.ui.editor.LinkDialog.prototype.showOpenLinkInNewWindow = function( + startChecked) { + this.showOpenLinkInNewWindow_ = true; + this.isOpenLinkInNewWindowChecked_ = startChecked; +}; + + +/** + * Tells the dialog to focus the text to display input instead of the url field + * if the text to display input is empty when the dialog is opened. + */ +goog.ui.editor.LinkDialog.prototype.focusTextToDisplayOnOpenIfEmpty = + function() { + this.focusTextToDisplayOnOpenIfEmpty_ = true; +}; + + +/** + * Tells the dialog to show a checkbox where the user can choose to add + * 'rel=nofollow' attribute to the link. + */ +goog.ui.editor.LinkDialog.prototype.showRelNoFollow = function() { + this.showRelNoFollow_ = true; +}; + + +/** @override */ +goog.ui.editor.LinkDialog.prototype.show = function() { + goog.ui.editor.LinkDialog.base(this, 'show'); + + + this.selectAppropriateTab_( + this.textToDisplayInput_.value, this.getTargetUrl_()); + + if (this.focusTextToDisplayOnOpenIfEmpty_ && + !this.targetLink_.getCurrentText()) { + goog.editor.focus.focusInputField(this.textToDisplayInput_); + } + + this.syncOkButton_(); + + if (this.showOpenLinkInNewWindow_) { + if (!this.targetLink_.isNew()) { + // If link is not new, checkbox should reflect current target. + this.isOpenLinkInNewWindowChecked_ = + this.targetLink_.getAnchor().target == '_blank'; + } + this.openInNewWindowCheckbox_.checked = this.isOpenLinkInNewWindowChecked_; + } + + if (this.showRelNoFollow_) { + this.relNoFollowCheckbox_.checked = + goog.ui.editor.LinkDialog.hasNoFollow(this.targetLink_.getAnchor().rel); + } +}; + + +/** @override */ +goog.ui.editor.LinkDialog.prototype.hide = function() { + this.disableAutogenFlag_(false); + goog.ui.editor.LinkDialog.base(this, 'hide'); +}; + + +/** + * Tells the dialog whether to show the 'text to display' div. + * When the target element of the dialog is an image, there is no link text + * to modify. This function can be used for this kind of situations. + * @param {boolean} visible Whether to make 'text to display' div visible. + */ +goog.ui.editor.LinkDialog.prototype.setTextToDisplayVisible = function( + visible) { + if (this.textToDisplayDiv_) { + goog.style.setStyle( + this.textToDisplayDiv_, 'display', visible ? 'block' : 'none'); + } +}; + + +/** + * Tells the plugin whether to stop leaking the page's url via the referrer + * header when the "test this link" link is clicked. + * @param {boolean} stop Whether to stop leaking the referrer. + */ +goog.ui.editor.LinkDialog.prototype.setStopReferrerLeaks = function(stop) { + this.stopReferrerLeaks_ = stop; +}; + + +/** + * Tells the dialog whether the autogeneration of text to display is to be + * enabled. + * @param {boolean} enable Whether to enable the feature. + */ +goog.ui.editor.LinkDialog.prototype.setAutogenFeatureEnabled = function( + enable) { + this.autogenFeatureEnabled_ = enable; +}; + + +/** + * Checks if `str` contains {@code "nofollow"} as a separate word. + * @param {string} str String to be tested. This is usually `rel` + * attribute of an `HTMLAnchorElement` object. + * @return {boolean} `true` if `str` contains `nofollow`. + */ +goog.ui.editor.LinkDialog.hasNoFollow = function(str) { + return goog.ui.editor.LinkDialog.NO_FOLLOW_REGEX_.test(str); +}; + + +/** + * Removes {@code "nofollow"} from `rel` if it's present as a separate + * word. + * @param {string} rel Input string. This is usually `rel` attribute of + * an `HTMLAnchorElement` object. + * @return {string} `rel` with any {@code "nofollow"} removed. + */ +goog.ui.editor.LinkDialog.removeNoFollow = function(rel) { + return rel.replace(goog.ui.editor.LinkDialog.NO_FOLLOW_REGEX_, ''); +}; + + +// *** Protected interface ************************************************** // + + +/** @override */ +goog.ui.editor.LinkDialog.prototype.createDialogControl = function() { + var builder = new goog.ui.editor.AbstractDialog.Builder(this); + builder.setTitle(goog.ui.editor.messages.MSG_EDIT_LINK) + .setContent(this.createDialogContent_()); + return builder.build(); +}; + + +/** + * Creates and returns the event object to be used when dispatching the OK + * event to listeners based on which tab is currently selected and the contents + * of the input fields of that tab. + * @return {!goog.ui.editor.LinkDialog.OkEvent} The event object to be used when + * dispatching the OK event to listeners. + * @protected + * @override + */ +goog.ui.editor.LinkDialog.prototype.createOkEvent = function() { + if (this.tabPane_.getCurrentTabId() == + goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS_TAB) { + return this.createOkEventFromEmailTab_(); + } else { + return this.createOkEventFromWebTab_(); + } +}; + + +// *** Private implementation *********************************************** // + + +/** + * Regular expression that matches `nofollow` value in an + * {@code * HTMLAnchorElement}'s `rel` element. + * @type {RegExp} + * @private + */ +goog.ui.editor.LinkDialog.NO_FOLLOW_REGEX_ = /\bnofollow\b/i; + + +/** + * Creates contents of this dialog. + * @return {!Element} Contents of the dialog as a DOM element. + * @private + */ +goog.ui.editor.LinkDialog.prototype.createDialogContent_ = function() { + this.textToDisplayDiv_ = + /** @type {!HTMLDivElement} */ (this.buildTextToDisplayDiv_()); + var content = + this.dom.createDom(goog.dom.TagName.DIV, null, this.textToDisplayDiv_); + + this.tabPane_ = + new goog.ui.editor.TabPane(this.dom, goog.ui.editor.messages.MSG_LINK_TO); + this.registerDisposable(this.tabPane_); + this.tabPane_.addTab( + goog.ui.editor.LinkDialog.Id_.ON_WEB_TAB, + goog.ui.editor.messages.MSG_ON_THE_WEB, + goog.ui.editor.messages.MSG_ON_THE_WEB_TIP, + goog.ui.editor.LinkDialog.BUTTON_GROUP_, this.buildTabOnTheWeb_()); + this.tabPane_.addTab( + goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS_TAB, + goog.ui.editor.messages.MSG_EMAIL_ADDRESS, + goog.ui.editor.messages.MSG_EMAIL_ADDRESS_TIP, + goog.ui.editor.LinkDialog.BUTTON_GROUP_, this.buildTabEmailAddress_()); + this.tabPane_.render(content); + + this.eventHandler_.listen( + this.tabPane_, goog.ui.Component.EventType.SELECT, this.onChangeTab_); + + if (this.showOpenLinkInNewWindow_) { + content.appendChild(this.buildOpenInNewWindowDiv_()); + } + if (this.showRelNoFollow_) { + content.appendChild(this.buildRelNoFollowDiv_()); + } + + return content; +}; + + +/** + * Builds and returns the text to display section of the edit link dialog. + * @return {!Element} A div element to be appended into the dialog div. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.editor.LinkDialog.prototype.buildTextToDisplayDiv_ = function() { + var table = this.dom.createTable(1, 2); + table.cellSpacing = '0'; + table.cellPadding = '0'; + table.style.fontSize = '10pt'; + // Build the text to display input. + var textToDisplayDiv = this.dom.createDom(goog.dom.TagName.DIV); + var html = goog.html.SafeHtml.create( + 'span', { + 'style': { + 'position': 'relative', + 'bottom': '2px', + 'padding-right': '1px', + 'white-space': 'nowrap' + }, + id: goog.ui.editor.LinkDialog.Id_.TEXT_TO_DISPLAY_LABEL + }, + [goog.ui.editor.messages.MSG_TEXT_TO_DISPLAY, goog.string.Unicode.NBSP]); + goog.dom.safe.setInnerHtml(table.rows[0].cells[0], html); + this.textToDisplayInput_ = this.dom.createDom( + goog.dom.TagName.INPUT, + {id: goog.ui.editor.LinkDialog.Id_.TEXT_TO_DISPLAY}); + var textInput = this.textToDisplayInput_; + // 98% prevents scroll bars in standards mode. + // TODO(robbyw): Is this necessary for quirks mode? + goog.style.setStyle(textInput, 'width', '98%'); + goog.style.setStyle(table.rows[0].cells[1], 'width', '100%'); + goog.dom.appendChild(table.rows[0].cells[1], textInput); + + goog.a11y.aria.setState( + /** @type {!Element} */ (textInput), goog.a11y.aria.State.LABELLEDBY, + goog.ui.editor.LinkDialog.Id_.TEXT_TO_DISPLAY_LABEL); + textInput.value = this.targetLink_.getCurrentText(); + + this.textInputHandler_ = new goog.events.InputHandler(textInput); + this.registerDisposable(this.textInputHandler_); + this.eventHandler_.listen( + this.textInputHandler_, goog.events.InputHandler.EventType.INPUT, + this.onTextToDisplayEdit_); + + goog.dom.appendChild(textToDisplayDiv, table); + return textToDisplayDiv; +}; + + +/** + * Builds and returns the "checkbox to open the link in a new window" section of + * the edit link dialog. + * @return {!Element} A div element to be appended into the dialog div. + * @private + */ +goog.ui.editor.LinkDialog.prototype.buildOpenInNewWindowDiv_ = function() { + this.openInNewWindowCheckbox_ = this.dom.createDom( + goog.dom.TagName.INPUT, {'type': goog.dom.InputType.CHECKBOX}); + return this.dom.createDom( + goog.dom.TagName.DIV, null, + this.dom.createDom( + goog.dom.TagName.LABEL, null, this.openInNewWindowCheckbox_, + goog.ui.editor.messages.MSG_OPEN_IN_NEW_WINDOW)); +}; + + +/** + * Creates a DIV with a checkbox for {@code rel=nofollow} option. + * @return {!Element} Newly created DIV element. + * @private + */ +goog.ui.editor.LinkDialog.prototype.buildRelNoFollowDiv_ = function() { + var formatter = new goog.html.SafeHtmlFormatter(); + /** @desc Checkbox text for adding 'rel=nofollow' attribute to a link. */ + var MSG_ADD_REL_NOFOLLOW_ATTR = goog.getMsg( + "Add '{$relNoFollow}' attribute ({$linkStart}Learn more{$linkEnd})", { + 'relNoFollow': 'rel=nofollow', + 'linkStart': formatter.startTag('a', { + 'href': 'http://support.google.com/webmasters/bin/' + + 'answer.py?hl=en&answer=96569', + 'target': '_blank' + }), + 'linkEnd': formatter.endTag('a') + }); + + this.relNoFollowCheckbox_ = this.dom.createDom( + goog.dom.TagName.INPUT, {'type': goog.dom.InputType.CHECKBOX}); + return this.dom.createDom( + goog.dom.TagName.DIV, null, + this.dom.createDom( + goog.dom.TagName.LABEL, null, this.relNoFollowCheckbox_, + goog.dom.safeHtmlToNode( + formatter.format(MSG_ADD_REL_NOFOLLOW_ATTR)))); +}; + + +/** +* Builds and returns the div containing the tab "On the web". +* @return {!Element} The div element containing the tab. +* @private +*/ +goog.ui.editor.LinkDialog.prototype.buildTabOnTheWeb_ = function() { + var onTheWebDiv = this.dom.createElement(goog.dom.TagName.DIV); + + var headingDiv = this.dom.createDom( + goog.dom.TagName.DIV, {}, + this.dom.createDom( + goog.dom.TagName.B, {}, goog.ui.editor.messages.MSG_WHAT_URL)); + var urlInput = this.dom.createDom(goog.dom.TagName.INPUT, { + id: goog.ui.editor.LinkDialog.Id_.ON_WEB_INPUT, + className: goog.ui.editor.LinkDialog.TARGET_INPUT_CLASSNAME_ + }); + goog.a11y.aria.setState( + urlInput, goog.a11y.aria.State.LABELLEDBY, + goog.ui.editor.LinkDialog.Id_.ON_WEB_TAB); + // IE throws on unknown values for type, but IE10+ supports type=url + if (!goog.userAgent.IE || goog.userAgent.isVersionOrHigher('10')) { + // On browsers that support Web Forms 2.0, allow autocompletion of URLs. + urlInput.type = goog.dom.InputType.URL; + } + + if (goog.editor.BrowserFeature.NEEDS_99_WIDTH_IN_STANDARDS_MODE && + goog.editor.node.isStandardsMode(urlInput)) { + urlInput.style.width = '99%'; + } + + var inputDiv = this.dom.createDom(goog.dom.TagName.DIV, null, urlInput); + + this.urlInputHandler_ = new goog.events.InputHandler(urlInput); + this.registerDisposable(this.urlInputHandler_); + this.eventHandler_.listen( + this.urlInputHandler_, goog.events.InputHandler.EventType.INPUT, + this.onUrlOrEmailInputChange_); + + var testLink = new goog.ui.Button( + goog.ui.editor.messages.MSG_TEST_THIS_LINK, + goog.ui.LinkButtonRenderer.getInstance(), this.dom); + testLink.render(inputDiv); + testLink.getElement().style.marginTop = '1em'; + this.eventHandler_.listen( + testLink, goog.ui.Component.EventType.ACTION, this.onWebTestLink_); + + // Build the "On the web" explanation text div. + var explanationDiv = this.dom.createDom( + goog.dom.TagName.DIV, + goog.ui.editor.LinkDialog.EXPLANATION_TEXT_CLASSNAME_); + goog.dom.safe.setInnerHtml( + explanationDiv, goog.ui.editor.messages.getTrLinkExplanationSafeHtml()); + onTheWebDiv.appendChild(headingDiv); + onTheWebDiv.appendChild(inputDiv); + onTheWebDiv.appendChild(explanationDiv); + + return onTheWebDiv; +}; + + +/** + * Builds and returns the div containing the tab "Email address". + * @return {!Element} the div element containing the tab. + * @private + */ +goog.ui.editor.LinkDialog.prototype.buildTabEmailAddress_ = function() { + var emailTab = this.dom.createDom(goog.dom.TagName.DIV); + + var headingDiv = this.dom.createDom( + goog.dom.TagName.DIV, {}, + this.dom.createDom( + goog.dom.TagName.B, {}, goog.ui.editor.messages.MSG_WHAT_EMAIL)); + goog.dom.appendChild(emailTab, headingDiv); + var emailInput = this.dom.createDom(goog.dom.TagName.INPUT, { + id: goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS_INPUT, + className: goog.ui.editor.LinkDialog.TARGET_INPUT_CLASSNAME_ + }); + goog.a11y.aria.setState( + emailInput, goog.a11y.aria.State.LABELLEDBY, + goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS_TAB); + + if (goog.editor.BrowserFeature.NEEDS_99_WIDTH_IN_STANDARDS_MODE && + goog.editor.node.isStandardsMode(emailInput)) { + // Standards mode sizes this too large. + emailInput.style.width = '99%'; + } + + goog.dom.appendChild(emailTab, emailInput); + + this.emailInputHandler_ = new goog.events.InputHandler(emailInput); + this.registerDisposable(this.emailInputHandler_); + this.eventHandler_.listen( + this.emailInputHandler_, goog.events.InputHandler.EventType.INPUT, + this.onUrlOrEmailInputChange_); + + goog.dom.appendChild( + emailTab, + this.dom.createDom( + goog.dom.TagName.DIV, { + id: goog.ui.editor.LinkDialog.Id_.EMAIL_WARNING, + className: goog.ui.editor.LinkDialog.EMAIL_WARNING_CLASSNAME_, + style: 'visibility:hidden' + }, + goog.ui.editor.messages.MSG_INVALID_EMAIL)); + + if (this.emailWarning_) { + var explanationDiv = this.dom.createDom( + goog.dom.TagName.DIV, + goog.ui.editor.LinkDialog.EXPLANATION_TEXT_CLASSNAME_); + goog.dom.safe.setInnerHtml(explanationDiv, this.emailWarning_); + goog.dom.appendChild(emailTab, explanationDiv); + } + return emailTab; +}; + + +/** + * Returns the url that the target points to. + * @return {string} The url that the target points to. + * @private + */ +goog.ui.editor.LinkDialog.prototype.getTargetUrl_ = function() { + // Get the href-attribute through getAttribute() rather than the href property + // because Google-Toolbar on Firefox with "Send with Gmail" turned on + // modifies the href-property of 'mailto:' links but leaves the attribute + // untouched. + return this.targetLink_.getAnchor().getAttribute('href') || ''; +}; + + +/** + * Selects the correct tab based on the URL, and fills in its inputs. + * For new links, it suggests a url based on the link text. + * @param {string} text The inner text of the link. + * @param {string} url The href for the link. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.editor.LinkDialog.prototype.selectAppropriateTab_ = function( + text, url) { + if (this.isNewLink_()) { + // Newly created non-empty link: try to infer URL from the link text. + this.guessUrlAndSelectTab_(text); + } else if (goog.editor.Link.isMailto(url)) { + // The link is for an email. + this.tabPane_.setSelectedTabId( + goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS_TAB); + this.dom.getElement(goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS_INPUT) + .value = url.substring(url.indexOf(':') + 1); + this.setAutogenFlagFromCurInput_(); + } else { + // No specific tab was appropriate, default to on the web tab. + this.tabPane_.setSelectedTabId(goog.ui.editor.LinkDialog.Id_.ON_WEB_TAB); + this.dom.getElement(goog.ui.editor.LinkDialog.Id_.ON_WEB_INPUT).value = + this.isNewLink_() ? 'http://' : url; + this.setAutogenFlagFromCurInput_(); + } +}; + + +/** + * Select a url/tab based on the link's text. This function is simply + * the isNewLink_() == true case of selectAppropriateTab_(). + * @param {string} text The inner text of the link. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.editor.LinkDialog.prototype.guessUrlAndSelectTab_ = function(text) { + if (goog.editor.Link.isLikelyEmailAddress(text)) { + // The text is for an email address. + this.tabPane_.setSelectedTabId( + goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS_TAB); + this.dom.getElement(goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS_INPUT) + .value = text; + this.setAutogenFlag_(true); + // TODO(user): Why disable right after enabling? What bug are we + // working around? + this.disableAutogenFlag_(true); + } else if (goog.editor.Link.isLikelyUrl(text)) { + // The text is for a web URL. + this.tabPane_.setSelectedTabId(goog.ui.editor.LinkDialog.Id_.ON_WEB_TAB); + this.dom.getElement(goog.ui.editor.LinkDialog.Id_.ON_WEB_INPUT).value = + text; + this.setAutogenFlag_(true); + this.disableAutogenFlag_(true); + } else { + // No meaning could be deduced from text, choose a default tab. + if (!this.targetLink_.getCurrentText()) { + this.setAutogenFlag_(true); + } + this.tabPane_.setSelectedTabId(goog.ui.editor.LinkDialog.Id_.ON_WEB_TAB); + } +}; + + +/** + * Called on a change to the url or email input. If either one of those tabs + * is active, sets the OK button to enabled/disabled accordingly. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.editor.LinkDialog.prototype.syncOkButton_ = function() { + var inputValue; + if (this.tabPane_.getCurrentTabId() == + goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS_TAB) { + inputValue = + this.dom.getElement(goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS_INPUT) + .value; + this.toggleInvalidEmailWarning_( + inputValue != '' && !goog.editor.Link.isLikelyEmailAddress(inputValue)); + } else if ( + this.tabPane_.getCurrentTabId() == + goog.ui.editor.LinkDialog.Id_.ON_WEB_TAB) { + inputValue = + this.dom.getElement(goog.ui.editor.LinkDialog.Id_.ON_WEB_INPUT).value; + } else { + return; + } + this.getOkButtonElement().disabled = + goog.string.isEmptyOrWhitespace(inputValue); +}; + + +/** + * Show/hide the Invalid Email Address warning. + * @param {boolean} on Whether to show the warning. + * @private + */ +goog.ui.editor.LinkDialog.prototype.toggleInvalidEmailWarning_ = function(on) { + this.dom.getElement(goog.ui.editor.LinkDialog.Id_.EMAIL_WARNING) + .style.visibility = (on ? 'visible' : 'hidden'); +}; + + +/** + * Changes the autogenerateTextToDisplay flag so that text to + * display stops autogenerating. + * @private + */ +goog.ui.editor.LinkDialog.prototype.onTextToDisplayEdit_ = function() { + var inputEmpty = this.textToDisplayInput_.value == ''; + if (inputEmpty) { + this.setAutogenFlag_(true); + } else { + this.setAutogenFlagFromCurInput_(); + } +}; + + +/** + * The function called when hitting OK with the "On the web" tab current. + * @return {!goog.ui.editor.LinkDialog.OkEvent} The event object to be used when + * dispatching the OK event to listeners. + * @private + */ +goog.ui.editor.LinkDialog.prototype.createOkEventFromWebTab_ = function() { + var input = /** @type {HTMLInputElement} */ ( + this.dom.getElement(goog.ui.editor.LinkDialog.Id_.ON_WEB_INPUT)); + var linkURL = input.value; + if (goog.editor.Link.isLikelyEmailAddress(linkURL)) { + // Make sure that if user types in an e-mail address, it becomes "mailto:". + return this.createOkEventFromEmailTab_( + goog.ui.editor.LinkDialog.Id_.ON_WEB_INPUT); + } else { + if (linkURL.search(/:/) < 0) { + linkURL = 'http://' + goog.string.trimLeft(linkURL); + } + return this.createOkEventFromUrl_(linkURL); + } +}; + + +/** + * The function called when hitting OK with the "email address" tab current. + * @param {string=} opt_inputId Id of an alternate input to check. + * @return {!goog.ui.editor.LinkDialog.OkEvent} The event object to be used when + * dispatching the OK event to listeners. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.editor.LinkDialog.prototype.createOkEventFromEmailTab_ = function( + opt_inputId) { + var linkURL = + this.dom + .getElement( + opt_inputId || goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS_INPUT) + .value; + linkURL = 'mailto:' + linkURL; + return this.createOkEventFromUrl_(linkURL); +}; + + +/** + * Function to test a link from the on the web tab. + * @private + */ +goog.ui.editor.LinkDialog.prototype.onWebTestLink_ = function() { + var input = /** @type {HTMLInputElement} */ ( + this.dom.getElement(goog.ui.editor.LinkDialog.Id_.ON_WEB_INPUT)); + var url = input.value; + if (url.search(/:/) < 0) { + url = 'http://' + goog.string.trimLeft(url); + } + if (this.dispatchEvent( + new goog.ui.editor.LinkDialog.BeforeTestLinkEvent(url))) { + var win = this.dom.getWindow(); + var size = goog.dom.getViewportSize(win); + var openOptions = { + target: '_blank', + width: Math.max(size.width - 50, 50), + height: Math.max(size.height - 50, 50), + toolbar: true, + scrollbars: true, + location: true, + statusbar: false, + menubar: true, 'resizable': true, 'noreferrer': this.stopReferrerLeaks_ + }; + goog.window.open(url, openOptions, win); + } +}; + + +/** + * Called whenever the url or email input is edited. If the text to display + * matches the text to display, turn on auto. Otherwise if auto is on, update + * the text to display based on the url. + * @private + */ +goog.ui.editor.LinkDialog.prototype.onUrlOrEmailInputChange_ = function() { + if (this.autogenerateTextToDisplay_) { + this.setTextToDisplayFromAuto_(); + } else if (this.textToDisplayInput_.value == '') { + this.setAutogenFlagFromCurInput_(); + } + this.syncOkButton_(); +}; + + +/** + * Called when the currently selected tab changes. + * @param {goog.events.Event} e The tab change event. + * @private + */ +goog.ui.editor.LinkDialog.prototype.onChangeTab_ = function(e) { + var tab = /** @type {goog.ui.Tab} */ (e.target); + + // Focus on the input field in the selected tab. + var input = /** @type {!HTMLElement} */ ( + this.dom.getElement( + tab.getId() + goog.ui.editor.LinkDialog.Id_.TAB_INPUT_SUFFIX)); + goog.editor.focus.focusInputField(input); + + // For some reason, IE does not fire onpropertychange events when the width + // is specified as a percentage, which breaks the InputHandlers. + input.style.width = ''; + input.style.width = input.offsetWidth + 'px'; + + this.syncOkButton_(); + this.setTextToDisplayFromAuto_(); +}; + + +/** + * If autogen is turned on, set the value of text to display based on the + * current selection or url. + * @private + */ +goog.ui.editor.LinkDialog.prototype.setTextToDisplayFromAuto_ = function() { + if (this.autogenFeatureEnabled_ && this.autogenerateTextToDisplay_) { + var inputId = this.tabPane_.getCurrentTabId() + + goog.ui.editor.LinkDialog.Id_.TAB_INPUT_SUFFIX; + this.textToDisplayInput_.value = + /** @type {HTMLInputElement} */ (this.dom.getElement(inputId)).value; + } +}; + + +/** + * Turn on the autogenerate text to display flag, and set some sort of indicator + * that autogen is on. + * @param {boolean} val Boolean value to set autogenerate to. + * @private + */ +goog.ui.editor.LinkDialog.prototype.setAutogenFlag_ = function(val) { + // TODO(user): This whole autogen thing is very confusing. It needs + // to be refactored and/or explained. + this.autogenerateTextToDisplay_ = val; +}; + + +/** + * Disables autogen so that onUrlOrEmailInputChange_ doesn't act in cases + * that are undesirable. + * @param {boolean} autogen Boolean value to set disableAutogen to. + * @private + */ +goog.ui.editor.LinkDialog.prototype.disableAutogenFlag_ = function(autogen) { + this.setAutogenFlag_(!autogen); + this.disableAutogen_ = autogen; +}; + + +/** + * Creates an OK event from the text to display input and the specified link. + * If text to display input is empty, then generate the auto value for it. + * @return {!goog.ui.editor.LinkDialog.OkEvent} The event object to be used when + * dispatching the OK event to listeners. + * @param {string} url Url the target element should point to. + * @private + */ +goog.ui.editor.LinkDialog.prototype.createOkEventFromUrl_ = function(url) { + // Fill in the text to display input in case it is empty. + this.setTextToDisplayFromAuto_(); + if (this.showOpenLinkInNewWindow_) { + // Save checkbox state for next time. + this.isOpenLinkInNewWindowChecked_ = this.openInNewWindowCheckbox_.checked; + } + return new goog.ui.editor.LinkDialog.OkEvent( + this.textToDisplayInput_.value, url, + this.showOpenLinkInNewWindow_ && this.isOpenLinkInNewWindowChecked_, + this.showRelNoFollow_ && this.relNoFollowCheckbox_.checked); +}; + + +/** + * If an email or url is being edited, set autogenerate to on if the text to + * display matches the url. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.editor.LinkDialog.prototype.setAutogenFlagFromCurInput_ = function() { + var autogen = false; + if (!this.disableAutogen_) { + var tabInput = this.dom.getElement( + this.tabPane_.getCurrentTabId() + + goog.ui.editor.LinkDialog.Id_.TAB_INPUT_SUFFIX); + autogen = (tabInput.value == this.textToDisplayInput_.value); + } + this.setAutogenFlag_(autogen); +}; + + +/** + * @return {boolean} Whether the link is new. + * @private + */ +goog.ui.editor.LinkDialog.prototype.isNewLink_ = function() { + return this.targetLink_.isNew(); +}; + + +/** + * IDs for relevant DOM elements. + * @enum {string} + * @private + */ +goog.ui.editor.LinkDialog.Id_ = { + TEXT_TO_DISPLAY: 'linkdialog-text', + TEXT_TO_DISPLAY_LABEL: 'linkdialog-text-label', + ON_WEB_TAB: 'linkdialog-onweb', + ON_WEB_INPUT: 'linkdialog-onweb-tab-input', + EMAIL_ADDRESS_TAB: 'linkdialog-email', + EMAIL_ADDRESS_INPUT: 'linkdialog-email-tab-input', + EMAIL_WARNING: 'linkdialog-email-warning', + TAB_INPUT_SUFFIX: '-tab-input' +}; + + +/** + * Base name for the radio buttons group. + * @type {string} + * @private + */ +goog.ui.editor.LinkDialog.BUTTON_GROUP_ = 'linkdialog-buttons'; + + +/** + * Class name for the url and email input elements. + * @type {string} + * @private + */ +goog.ui.editor.LinkDialog.TARGET_INPUT_CLASSNAME_ = + goog.getCssName('tr-link-dialog-target-input'); + + +/** + * Class name for the email address warning element. + * @type {string} + * @private + */ +goog.ui.editor.LinkDialog.EMAIL_WARNING_CLASSNAME_ = + goog.getCssName('tr-link-dialog-email-warning'); + + +/** + * Class name for the explanation text elements. + * @type {string} + * @private + */ +goog.ui.editor.LinkDialog.EXPLANATION_TEXT_CLASSNAME_ = + goog.getCssName('tr-link-dialog-explanation-text'); diff --git a/closure-library/closure/goog/ui/editor/messages.js b/closure-library/closure/goog/ui/editor/messages.js new file mode 100644 index 0000000000..235c0e88ea --- /dev/null +++ b/closure-library/closure/goog/ui/editor/messages.js @@ -0,0 +1,147 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Messages common to Editor UI components. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.ui.editor.messages'); + +goog.require('goog.html.SafeHtmlFormatter'); + + +/** @desc Link button / bubble caption. */ +goog.ui.editor.messages.MSG_LINK_CAPTION = goog.getMsg('Link'); + + +/** @desc Title for the dialog that edits a link. */ +goog.ui.editor.messages.MSG_EDIT_LINK = goog.getMsg('Edit Link'); + + +/** @desc Prompt the user for the text of the link they've written. */ +goog.ui.editor.messages.MSG_TEXT_TO_DISPLAY = goog.getMsg('Text to display:'); + + +/** @desc Prompt the user for the URL of the link they've created. */ +goog.ui.editor.messages.MSG_LINK_TO = goog.getMsg('Link to:'); + + +/** @desc Prompt the user to type a web address for their link. */ +goog.ui.editor.messages.MSG_ON_THE_WEB = goog.getMsg('Web address'); + + +/** @desc More details on what linking to a web address involves.. */ +goog.ui.editor.messages.MSG_ON_THE_WEB_TIP = + goog.getMsg('Link to a page or file somewhere else on the web'); + + +/** + * @desc Text for a button that allows the user to test the link that + * they created. + */ +goog.ui.editor.messages.MSG_TEST_THIS_LINK = goog.getMsg('Test this link'); + + +/** + * @return {!goog.html.SafeHtml} SafeHtml version of MSG_TR_LINK_EXPLANATION. + */ +goog.ui.editor.messages.getTrLinkExplanationSafeHtml = function() { + var formatter = new goog.html.SafeHtmlFormatter(); + + /** + * @desc Explanation for how to create a link with the link-editing dialog. + */ + var MSG_TR_LINK_EXPLANATION = goog.getMsg( + '{$startBold}Not sure what to put in the box?{$endBold} ' + + 'First, find the page on the web that you want to ' + + 'link to. (A {$searchEngineLink}search engine{$endLink} ' + + 'might be useful.) Then, copy the web address from ' + + 'the box in your browser\'s address bar, and paste it into ' + + 'the box above.', + { + 'startBold': formatter.startTag('b'), + 'endBold': formatter.endTag('b'), + 'searchEngineLink': formatter.startTag( + 'a', {'href': 'http://www.google.com/', 'target': '_new'}), + 'endLink': formatter.endTag('a') + }); + + return formatter.format(MSG_TR_LINK_EXPLANATION); +}; + + +/** @desc Prompt for the URL of a link that the user is creating. */ +goog.ui.editor.messages.MSG_WHAT_URL = + goog.getMsg('To what URL should this link go?'); + + +/** + * @desc Prompt for an email address, so that the user can create a link + * that sends an email. + */ +goog.ui.editor.messages.MSG_EMAIL_ADDRESS = goog.getMsg('Email address'); + + +/** + * @desc Explanation of the prompt for an email address in a link. + */ +goog.ui.editor.messages.MSG_EMAIL_ADDRESS_TIP = + goog.getMsg('Link to an email address'); + + +/** @desc Error message when the user enters an invalid email address. */ +goog.ui.editor.messages.MSG_INVALID_EMAIL = + goog.getMsg('Invalid email address'); + + +/** + * @desc When the user creates a mailto link, asks them what email + * address clicking on this link will send mail to. + */ +goog.ui.editor.messages.MSG_WHAT_EMAIL = + goog.getMsg('To what email address should this link?'); + + +/** + * @return {!goog.html.SafeHtml} SafeHtml version of MSG_EMAIL_EXPLANATION. + */ +goog.ui.editor.messages.getEmailExplanationSafeHtml = function() { + var formatter = new goog.html.SafeHtmlFormatter(); + + /** + * @desc Warning about the dangers of creating links with email + * addresses in them. + */ + var MSG_EMAIL_EXPLANATION = goog.getMsg( + '{$preb}Be careful.{$postb} ' + + 'Remember that any time you include an email address on a web ' + + 'page, nasty spammers can find it too.', + {'preb': formatter.startTag('b'), 'postb': formatter.endTag('b')}); + + return formatter.format(MSG_EMAIL_EXPLANATION); +}; + + +/** + * @desc Label for the checkbox that allows the user to specify what when this + * link is clicked, it should be opened in a new window. + */ +goog.ui.editor.messages.MSG_OPEN_IN_NEW_WINDOW = + goog.getMsg('Open this link in a new window'); + + +/** @desc Image bubble caption. */ +goog.ui.editor.messages.MSG_IMAGE_CAPTION = goog.getMsg('Image'); diff --git a/closure-library/closure/goog/ui/editor/tabpane.js b/closure-library/closure/goog/ui/editor/tabpane.js new file mode 100644 index 0000000000..69fa468dbc --- /dev/null +++ b/closure-library/closure/goog/ui/editor/tabpane.js @@ -0,0 +1,201 @@ +// Copyright 2010 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Tabbed pane with style and functionality specific to + * Editor dialogs. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.ui.editor.TabPane'); + +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.InputType'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.classlist'); +goog.require('goog.events.EventHandler'); +goog.require('goog.events.EventType'); +goog.require('goog.style'); +goog.require('goog.ui.Component'); +goog.require('goog.ui.Control'); +goog.require('goog.ui.Tab'); +goog.require('goog.ui.TabBar'); + + + +/** + * Creates a new Editor-style tab pane. + * @param {goog.dom.DomHelper} dom The dom helper for the window to create this + * tab pane in. + * @param {string=} opt_caption Optional caption of the tab pane. + * @constructor + * @extends {goog.ui.Component} + * @final + */ +goog.ui.editor.TabPane = function(dom, opt_caption) { + goog.ui.editor.TabPane.base(this, 'constructor', dom); + + /** + * The event handler used to register events. + * @type {goog.events.EventHandler} + * @private + */ + this.eventHandler_ = new goog.events.EventHandler(this); + this.registerDisposable(this.eventHandler_); + + /** + * The tab bar used to render the tabs. + * @type {goog.ui.TabBar} + * @private + */ + this.tabBar_ = + new goog.ui.TabBar(goog.ui.TabBar.Location.START, undefined, this.dom_); + this.tabBar_.setFocusable(false); + + /** + * The content element. + * @private + */ + this.tabContent_ = this.dom_.createDom( + goog.dom.TagName.DIV, {className: goog.getCssName('goog-tab-content')}); + + /** + * The currently selected radio button. + * @type {Element} + * @private + */ + this.selectedRadio_ = null; + + /** + * The currently visible tab content. + * @type {Element} + * @private + */ + this.visibleContent_ = null; + + + // Add the caption as the first element in the tab bar. + if (opt_caption) { + var captionControl = new goog.ui.Control(opt_caption, undefined, this.dom_); + captionControl.addClassName(goog.getCssName('tr-tabpane-caption')); + captionControl.setEnabled(false); + this.tabBar_.addChild(captionControl, true); + } +}; +goog.inherits(goog.ui.editor.TabPane, goog.ui.Component); + + +/** + * @return {string} The ID of the content element for the current tab. + */ +goog.ui.editor.TabPane.prototype.getCurrentTabId = function() { + return this.tabBar_.getSelectedTab().getId(); +}; + + +/** + * Selects the tab with the given id. + * @param {string} id Id of the tab to select. + */ +goog.ui.editor.TabPane.prototype.setSelectedTabId = function(id) { + this.tabBar_.setSelectedTab(this.tabBar_.getChild(id)); +}; + + +/** + * Adds a tab to the tab pane. + * @param {string} id The id of the tab to add. + * @param {string} caption The caption of the tab. + * @param {string} tooltip The tooltip for the tab. + * @param {string} groupName for the radio button group. + * @param {Element} content The content element to show when this tab is + * selected. + */ +goog.ui.editor.TabPane.prototype.addTab = function( + id, caption, tooltip, groupName, content) { + var radio = this.dom_.createDom( + goog.dom.TagName.INPUT, + {name: groupName, type: goog.dom.InputType.RADIO}); + + var tab = new goog.ui.Tab( + [radio, this.dom_.createTextNode(caption)], undefined, this.dom_); + tab.setId(id); + tab.setTooltip(tooltip); + this.tabBar_.addChild(tab, true); + + // When you navigate the radio buttons with TAB and then the Arrow keys on + // Chrome and FF, you get a CLICK event on them, and the radio button + // is selected. You don't get a SELECT at all. We listen for SELECT + // nonetheless because it's possible that some browser will issue only + // SELECT. + this.eventHandler_.listen( + radio, [goog.events.EventType.SELECT, goog.events.EventType.CLICK], + goog.bind(this.tabBar_.setSelectedTab, this.tabBar_, tab)); + + content.id = id + '-tab'; + this.tabContent_.appendChild(content); + goog.style.setElementShown(content, false); +}; + + +/** @override */ +goog.ui.editor.TabPane.prototype.enterDocument = function() { + goog.ui.editor.TabPane.base(this, 'enterDocument'); + + // Get the root element and add a class name to it. + var root = this.getElement(); + goog.asserts.assert(root); + goog.dom.classlist.add(root, goog.getCssName('tr-tabpane')); + + // Add the tabs. + this.addChild(this.tabBar_, true); + this.eventHandler_.listen( + this.tabBar_, goog.ui.Component.EventType.SELECT, this.handleTabSelect_); + + // Add the tab content. + root.appendChild(this.tabContent_); + + // Add an element to clear the tab float. + root.appendChild(this.dom_.createDom(goog.dom.TagName.DIV, { + className: goog.getCssName('goog-tab-bar-clear') + })); +}; + + +/** + * Handles a tab change. + * @param {goog.events.Event} e The browser change event. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.editor.TabPane.prototype.handleTabSelect_ = function(e) { + var tab = /** @type {goog.ui.Tab} */ (e.target); + + // Show the tab content. + if (this.visibleContent_) { + goog.style.setElementShown(this.visibleContent_, false); + } + this.visibleContent_ = this.dom_.getElement(tab.getId() + '-tab'); + goog.style.setElementShown(this.visibleContent_, true); + + // Select the appropriate radio button (and deselect the current one). + if (this.selectedRadio_) { + this.selectedRadio_.checked = false; + } + this.selectedRadio_ = goog.dom.getElementsByTagName( + goog.dom.TagName.INPUT, tab.getElementStrict())[0]; + this.selectedRadio_.checked = true; +}; diff --git a/closure-library/closure/goog/ui/editor/toolbarcontroller.js b/closure-library/closure/goog/ui/editor/toolbarcontroller.js new file mode 100644 index 0000000000..b5ee12fa4b --- /dev/null +++ b/closure-library/closure/goog/ui/editor/toolbarcontroller.js @@ -0,0 +1,297 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A class for managing the editor toolbar. + * + * @author attila@google.com (Attila Bodis) + * @see ../../demos/editor/editor.html + */ + +goog.provide('goog.ui.editor.ToolbarController'); + +goog.require('goog.editor.Field'); +goog.require('goog.events.EventHandler'); +goog.require('goog.events.EventTarget'); +goog.require('goog.ui.Component'); + + + +/** + * A class for managing the editor toolbar. Acts as a bridge between + * a {@link goog.editor.Field} and a {@link goog.ui.Toolbar}. + * + * The `toolbar` argument must be an instance of {@link goog.ui.Toolbar} + * or a subclass. This class doesn't care how the toolbar was created. As + * long as one or more controls hosted in the toolbar have IDs that match + * built-in {@link goog.editor.Command}s, they will function as expected. It is + * the caller's responsibility to ensure that the toolbar is already rendered + * or that it decorates an existing element. + * + * + * @param {!goog.editor.Field} field Editable field to be controlled by the + * toolbar. + * @param {!goog.ui.Toolbar} toolbar Toolbar to control the editable field. + * @constructor + * @extends {goog.events.EventTarget} + */ +goog.ui.editor.ToolbarController = function(field, toolbar) { + goog.events.EventTarget.call(this); + + /** + * Event handler to listen for field events and user actions. + * @type {!goog.events.EventHandler} + * @private + */ + this.handler_ = new goog.events.EventHandler(this); + + /** + * The field instance controlled by the toolbar. + * @type {!goog.editor.Field} + * @private + */ + this.field_ = field; + + /** + * The toolbar that controls the field. + * @type {!goog.ui.Toolbar} + * @private + */ + this.toolbar_ = toolbar; + + /** + * Editing commands whose state is to be queried when updating the toolbar. + * @type {!Array} + * @private + */ + this.queryCommands_ = []; + + // Iterate over all buttons, and find those which correspond to + // queryable commands. Add them to the list of commands to query on + // each COMMAND_VALUE_CHANGE event. + this.toolbar_.forEachChild(function(button) { + if (button.queryable) { + this.queryCommands_.push(this.getComponentId(button.getId())); + } + }, this); + + // Make sure the toolbar doesn't steal keyboard focus. + this.toolbar_.setFocusable(false); + + // Hook up handlers that update the toolbar in response to field events, + // and to execute editor commands in response to toolbar events. + this.handler_ + .listen( + this.field_, goog.editor.Field.EventType.COMMAND_VALUE_CHANGE, + this.updateToolbar) + .listen( + this.toolbar_, goog.ui.Component.EventType.ACTION, this.handleAction); +}; +goog.inherits(goog.ui.editor.ToolbarController, goog.events.EventTarget); + + +/** + * Returns the Closure component ID of the control that corresponds to the + * given {@link goog.editor.Command} constant. + * Subclasses may override this method if they want to use a custom mapping + * scheme from commands to controls. + * @param {string} command Editor command. + * @return {string} Closure component ID of the corresponding toolbar + * control, if any. + * @protected + */ +goog.ui.editor.ToolbarController.prototype.getComponentId = function(command) { + // The default implementation assumes that the component ID is the same as + // the command constant. + return command; +}; + + +/** + * Returns the {@link goog.editor.Command} constant + * that corresponds to the given Closure component ID. Subclasses may override + * this method if they want to use a custom mapping scheme from controls to + * commands. + * @param {string} id Closure component ID of a toolbar control. + * @return {string} Editor command or dialog constant corresponding to the + * toolbar control, if any. + * @protected + */ +goog.ui.editor.ToolbarController.prototype.getCommand = function(id) { + // The default implementation assumes that the component ID is the same as + // the command constant. + return id; +}; + + +/** + * Returns the event handler object for the editor toolbar. Useful for classes + * that extend `goog.ui.editor.ToolbarController`. + * @return {!goog.events.EventHandler} The event handler object. + * @protected + * @this {T} + * @template T + */ +goog.ui.editor.ToolbarController.prototype.getHandler = function() { + return this.handler_; +}; + + +/** + * Returns the field instance managed by the toolbar. Useful for + * classes that extend `goog.ui.editor.ToolbarController`. + * @return {!goog.editor.Field} The field managed by the toolbar. + * @protected + */ +goog.ui.editor.ToolbarController.prototype.getField = function() { + return this.field_; +}; + + +/** + * Returns the toolbar UI component that manages the editor. Useful for + * classes that extend `goog.ui.editor.ToolbarController`. + * @return {!goog.ui.Toolbar} The toolbar UI component. + */ +goog.ui.editor.ToolbarController.prototype.getToolbar = function() { + return this.toolbar_; +}; + + +/** + * @return {boolean} Whether the toolbar is visible. + */ +goog.ui.editor.ToolbarController.prototype.isVisible = function() { + return this.toolbar_.isVisible(); +}; + + +/** + * Shows or hides the toolbar. + * @param {boolean} visible Whether to show or hide the toolbar. + */ +goog.ui.editor.ToolbarController.prototype.setVisible = function(visible) { + this.toolbar_.setVisible(visible); +}; + + +/** + * @return {boolean} Whether the toolbar is enabled. + */ +goog.ui.editor.ToolbarController.prototype.isEnabled = function() { + return this.toolbar_.isEnabled(); +}; + + +/** + * Enables or disables the toolbar. + * @param {boolean} enabled Whether to enable or disable the toolbar. + */ +goog.ui.editor.ToolbarController.prototype.setEnabled = function(enabled) { + this.toolbar_.setEnabled(enabled); +}; + + +/** + * Programmatically blurs the editor toolbar, un-highlighting the currently + * highlighted item, and closing the currently open menu (if any). + */ +goog.ui.editor.ToolbarController.prototype.blur = function() { + // We can't just call this.toolbar_.getElement().blur(), because the toolbar + // element itself isn't focusable, so goog.ui.Container#handleBlur isn't + // registered to handle blur events. + this.toolbar_.handleBlur(null); +}; + + +/** @override */ +goog.ui.editor.ToolbarController.prototype.disposeInternal = function() { + goog.ui.editor.ToolbarController.superClass_.disposeInternal.call(this); + if (this.handler_) { + this.handler_.dispose(); + delete this.handler_; + } + if (this.toolbar_) { + this.toolbar_.dispose(); + delete this.toolbar_; + } + delete this.field_; + delete this.queryCommands_; +}; + + +/** + * Updates the toolbar in response to editor events. Specifically, updates + * button states based on `COMMAND_VALUE_CHANGE` events, reflecting the + * effective formatting of the selection. + * @param {goog.events.Event} e Editor event to handle. + * @protected + */ +goog.ui.editor.ToolbarController.prototype.updateToolbar = function(e) { + if (!this.toolbar_.isEnabled() || !this.field_.isSelectionEditable() || + !this.dispatchEvent(goog.ui.Component.EventType.CHANGE)) { + return; + } + + var state; + + + try { + /** @type {Array} */ + e.commands; // Added by dispatchEvent. + + // If the COMMAND_VALUE_CHANGE event specifies which commands changed + // state, then we only need to update those ones, otherwise update all + // commands. + state = /** @type {Object} */ ( + this.field_.queryCommandValue(e.commands || this.queryCommands_)); + } catch (ex) { + // TODO(attila): Find out when/why this happens. + state = {}; + } + + this.updateToolbarFromState(state); +}; + + +/** + * Updates the toolbar to reflect a given state. + * @param {Object} state Object mapping editor commands to values. + */ +goog.ui.editor.ToolbarController.prototype.updateToolbarFromState = function( + state) { + for (var command in state) { + var button = this.toolbar_.getChild(this.getComponentId(command)); + if (button) { + var value = state[command]; + if (button.updateFromValue) { + button.updateFromValue(value); + } else { + button.setChecked(!!value); + } + } + } +}; + + +/** + * Handles `ACTION` events dispatched by toolbar buttons in response to + * user actions by executing the corresponding field command. + * @param {goog.events.Event} e Action event to handle. + * @protected + */ +goog.ui.editor.ToolbarController.prototype.handleAction = function(e) { + var command = this.getCommand(e.target.getId()); + this.field_.execCommand(command, e.target.getValue()); +}; diff --git a/closure-library/closure/goog/ui/editor/toolbarfactory.js b/closure-library/closure/goog/ui/editor/toolbarfactory.js new file mode 100644 index 0000000000..9b5103e150 --- /dev/null +++ b/closure-library/closure/goog/ui/editor/toolbarfactory.js @@ -0,0 +1,434 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Generic factory functions for creating the building blocks for + * an editor toolbar. + * + * @author attila@google.com (Attila Bodis) + */ + +goog.provide('goog.ui.editor.ToolbarFactory'); + +goog.require('goog.array'); +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.string'); +goog.require('goog.string.Unicode'); +goog.require('goog.style'); +goog.require('goog.ui.Component'); +goog.require('goog.ui.Container'); +goog.require('goog.ui.Option'); +goog.require('goog.ui.Toolbar'); +goog.require('goog.ui.ToolbarButton'); +goog.require('goog.ui.ToolbarColorMenuButton'); +goog.require('goog.ui.ToolbarMenuButton'); +goog.require('goog.ui.ToolbarRenderer'); +goog.require('goog.ui.ToolbarSelect'); +goog.require('goog.userAgent'); + + +/** + * Takes a font spec (e.g. "Arial, Helvetica, sans-serif") and returns the + * primary font name, normalized to lowercase (e.g. "arial"). + * @param {string} fontSpec Font specification. + * @return {string} The primary font name, in lowercase. + */ +goog.ui.editor.ToolbarFactory.getPrimaryFont = function(fontSpec) { + var i = fontSpec.indexOf(','); + var fontName = (i != -1 ? fontSpec.substring(0, i) : fontSpec).toLowerCase(); + // Strip leading/trailing quotes from the font name (bug 1050118). + return goog.string.stripQuotes(fontName, '"\''); +}; + + +/** + * Bulk-adds fonts to the given font menu button. The argument must be an + * array of font descriptor objects, each of which must have the following + * attributes: + *
        + *
      • `caption` - Caption to show in the font menu (e.g. 'Tahoma') + *
      • `value` - Value for the corresponding 'font-family' CSS style + * (e.g. 'Tahoma, Arial, sans-serif') + *
      + * @param {!goog.ui.Select} button Font menu button. + * @param {!Array<{caption: string, value: string}>} fonts Array of + * font descriptors. + */ +goog.ui.editor.ToolbarFactory.addFonts = function(button, fonts) { + goog.array.forEach(fonts, function(font) { + goog.ui.editor.ToolbarFactory.addFont(button, font.caption, font.value); + }); +}; + + +/** + * Adds a menu item to the given font menu button. The first font listed in + * the `value` argument is considered the font ID, so adding two items + * whose CSS style starts with the same font may lead to unpredictable results. + * @param {!goog.ui.Select} button Font menu button. + * @param {string} caption Caption to show for the font menu. + * @param {string} value Value for the corresponding 'font-family' CSS style. + */ +goog.ui.editor.ToolbarFactory.addFont = function(button, caption, value) { + // The font ID is the first font listed in the CSS style, normalized to + // lowercase. + var id = goog.ui.editor.ToolbarFactory.getPrimaryFont(value); + + // Construct the option, and add it to the button. + var option = new goog.ui.Option(caption, value, button.getDomHelper()); + option.setId(id); + button.addItem(option); + + // Captions are shown in their own font. + option.getContentElement().style.fontFamily = value; +}; + + +/** + * Bulk-adds font sizes to the given font size menu button. The argument must + * be an array of font size descriptor objects, each of which must have the + * following attributes: + *
        + *
      • `caption` - Caption to show in the font size menu (e.g. 'Huge') + *
      • `value` - Value for the corresponding HTML font size (e.g. 6) + *
      + * @param {!goog.ui.Select} button Font size menu button. + * @param {!Array<{caption: string, value:number}>} sizes Array of font + * size descriptors. + */ +goog.ui.editor.ToolbarFactory.addFontSizes = function(button, sizes) { + goog.array.forEach(sizes, function(size) { + goog.ui.editor.ToolbarFactory.addFontSize(button, size.caption, size.value); + }); +}; + + +/** + * Adds a menu item to the given font size menu button. The `value` + * argument must be a legacy HTML font size in the 0-7 range. + * @param {!goog.ui.Select} button Font size menu button. + * @param {string} caption Caption to show in the font size menu. + * @param {number} value Value for the corresponding HTML font size. + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.editor.ToolbarFactory.addFontSize = function(button, caption, value) { + // Construct the option, and add it to the button. + var option = new goog.ui.Option(caption, value, button.getDomHelper()); + button.addItem(option); + + // Adjust the font size of the menu item and the height of the checkbox + // element after they've been rendered by addItem(). Captions are shown in + // the corresponding font size, and lining up the checkbox is tricky. + var content = option.getContentElement(); + content.style.fontSize = + goog.ui.editor.ToolbarFactory.getPxFromLegacySize(value) + 'px'; + content.firstChild.style.height = '1.1em'; +}; + + +/** + * Converts a legacy font size specification into an equivalent pixel size. + * For example, {@code <font size="6">} is {@code font-size: 32px;}, etc. + * @param {number} fontSize Legacy font size spec in the 0-7 range. + * @return {number} Equivalent pixel size. + */ +goog.ui.editor.ToolbarFactory.getPxFromLegacySize = function(fontSize) { + return goog.ui.editor.ToolbarFactory.LEGACY_SIZE_TO_PX_MAP_[fontSize] || 10; +}; + + +/** + * Converts a pixel font size specification into an equivalent legacy size. + * For example, {@code font-size: 32px;} is {@code <font size="6">}, etc. + * If the given pixel size doesn't exactly match one of the legacy sizes, -1 is + * returned. + * @param {number} px Pixel font size. + * @return {number} Equivalent legacy size spec in the 0-7 range, or -1 if none + * exists. + */ +goog.ui.editor.ToolbarFactory.getLegacySizeFromPx = function(px) { + // Use lastIndexOf to get the largest legacy size matching the pixel size + // (most notably returning 1 instead of 0 for 10px). + return goog.array.lastIndexOf( + goog.ui.editor.ToolbarFactory.LEGACY_SIZE_TO_PX_MAP_, px); +}; + + +/** + * Map of legacy font sizes (0-7) to equivalent pixel sizes. + * @type {!Array} + * @private + */ +goog.ui.editor.ToolbarFactory.LEGACY_SIZE_TO_PX_MAP_ = + [10, 10, 13, 16, 18, 24, 32, 48]; + + +/** + * Bulk-adds format options to the given "Format block" menu button. The + * argument must be an array of format option descriptor objects, each of + * which must have the following attributes: + *
        + *
      • `caption` - Caption to show in the menu (e.g. 'Minor heading') + *
      • `command` - Corresponding {@link goog.dom.TagName} (e.g. + * 'H4') + *
      + * @param {!goog.ui.Select} button "Format block" menu button. + * @param {!Array<{caption: string, command: !goog.dom.TagName}>} formats Array + * of format option descriptors. + */ +goog.ui.editor.ToolbarFactory.addFormatOptions = function(button, formats) { + goog.array.forEach(formats, function(format) { + goog.ui.editor.ToolbarFactory.addFormatOption( + button, format.caption, format.command); + }); +}; + + +/** + * Adds a menu item to the given "Format block" menu button. + * @param {!goog.ui.Select} button "Format block" menu button. + * @param {string} caption Caption to show in the menu. + * @param {!goog.dom.TagName} tag Corresponding block format tag. + */ +goog.ui.editor.ToolbarFactory.addFormatOption = function(button, caption, tag) { + // Construct the option, and add it to the button. + // TODO(attila): Create boring but functional menu item for now... + var buttonDom = button.getDomHelper(); + var option = new goog.ui.Option( + buttonDom.createDom(goog.dom.TagName.DIV, null, caption), tag, buttonDom); + option.setId(String(tag)); + button.addItem(option); +}; + + +/** + * Creates a {@link goog.ui.Toolbar} containing the specified set of + * toolbar buttons, and renders it into the given parent element. Each + * item in the `items` array must a {@link goog.ui.Control}. + * @param {!Array} items Toolbar items; each must + * be a {@link goog.ui.Control}. + * @param {!Element} elem Toolbar parent element. + * @param {boolean=} opt_isRightToLeft Whether the editor chrome is + * right-to-left; defaults to the directionality of the toolbar parent + * element. + * @return {!goog.ui.Toolbar} Editor toolbar, rendered into the given parent + * element. + */ +goog.ui.editor.ToolbarFactory.makeToolbar = function( + items, elem, opt_isRightToLeft) { + var domHelper = goog.dom.getDomHelper(elem); + + // Create an empty horizontal toolbar using the default renderer. + var toolbar = new goog.ui.Toolbar( + goog.ui.ToolbarRenderer.getInstance(), + goog.ui.Container.Orientation.HORIZONTAL, domHelper); + + // Optimization: Explicitly test for the directionality of the parent + // element here, so we can set it for both the toolbar and its children, + // saving a lot of expensive calls to goog.style.isRightToLeft() during + // rendering. + var isRightToLeft = opt_isRightToLeft || goog.style.isRightToLeft(elem); + toolbar.setRightToLeft(isRightToLeft); + + // Optimization: Set the toolbar to non-focusable before it is rendered, + // to avoid creating unnecessary keyboard event handler objects. + toolbar.setFocusable(false); + + for (var i = 0, button; button = items[i]; i++) { + // Optimization: Set the button to non-focusable before it is rendered, + // to avoid creating unnecessary keyboard event handler objects. Also set + // the directionality of the button explicitly, to avoid expensive calls + // to goog.style.isRightToLeft() during rendering. + button.setSupportedState(goog.ui.Component.State.FOCUSED, false); + button.setRightToLeft(isRightToLeft); + toolbar.addChild(button, true); + } + + toolbar.render(elem); + return toolbar; +}; + + +/** + * Creates a toolbar button with the given ID, tooltip, and caption. Applies + * any custom CSS class names to the button's caption element. + * @param {string} id Button ID; must equal a {@link goog.editor.Command} for + * built-in buttons, anything else for custom buttons. + * @param {string} tooltip Tooltip to be shown on hover. + * @param {goog.ui.ControlContent} caption Button caption. + * @param {string=} opt_classNames CSS class name(s) to apply to the caption + * element. + * @param {goog.ui.ButtonRenderer=} opt_renderer Button renderer; defaults to + * {@link goog.ui.ToolbarButtonRenderer} if unspecified. + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM + * creation; defaults to the current document if unspecified. + * @return {!goog.ui.Button} A toolbar button. + */ +goog.ui.editor.ToolbarFactory.makeButton = function( + id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper) { + var button = new goog.ui.ToolbarButton( + goog.ui.editor.ToolbarFactory.createContent_( + caption, opt_classNames, opt_domHelper), + opt_renderer, opt_domHelper); + button.setId(id); + button.setTooltip(tooltip); + return button; +}; + + +/** + * Creates a toggle button with the given ID, tooltip, and caption. Applies + * any custom CSS class names to the button's caption element. The button + * returned has checkbox-like toggle semantics. + * @param {string} id Button ID; must equal a {@link goog.editor.Command} for + * built-in buttons, anything else for custom buttons. + * @param {string} tooltip Tooltip to be shown on hover. + * @param {goog.ui.ControlContent} caption Button caption. + * @param {string=} opt_classNames CSS class name(s) to apply to the caption + * element. + * @param {goog.ui.ButtonRenderer=} opt_renderer Button renderer; defaults to + * {@link goog.ui.ToolbarButtonRenderer} if unspecified. + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM + * creation; defaults to the current document if unspecified. + * @return {!goog.ui.Button} A toggle button. + */ +goog.ui.editor.ToolbarFactory.makeToggleButton = function( + id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper) { + var button = goog.ui.editor.ToolbarFactory.makeButton( + id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper); + button.setSupportedState(goog.ui.Component.State.CHECKED, true); + return button; +}; + + +/** + * Creates a menu button with the given ID, tooltip, and caption. Applies + * any custom CSS class names to the button's caption element. The button + * returned doesn't have an actual menu attached; use {@link + * goog.ui.MenuButton#setMenu} to attach a {@link goog.ui.Menu} to the + * button. + * @param {string} id Button ID; must equal a {@link goog.editor.Command} for + * built-in buttons, anything else for custom buttons. + * @param {string} tooltip Tooltip to be shown on hover. + * @param {goog.ui.ControlContent} caption Button caption. + * @param {string=} opt_classNames CSS class name(s) to apply to the caption + * element. + * @param {goog.ui.ButtonRenderer=} opt_renderer Button renderer; defaults to + * {@link goog.ui.ToolbarMenuButtonRenderer} if unspecified. + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM + * creation; defaults to the current document if unspecified. + * @return {!goog.ui.MenuButton} A menu button. + */ +goog.ui.editor.ToolbarFactory.makeMenuButton = function( + id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper) { + var button = new goog.ui.ToolbarMenuButton( + goog.ui.editor.ToolbarFactory.createContent_( + caption, opt_classNames, opt_domHelper), + null, opt_renderer, opt_domHelper); + button.setId(id); + button.setTooltip(tooltip); + return button; +}; + + +/** + * Creates a select button with the given ID, tooltip, and caption. Applies + * any custom CSS class names to the button's root element. The button + * returned doesn't have an actual menu attached; use {@link + * goog.ui.Select#setMenu} to attach a {@link goog.ui.Menu} containing + * {@link goog.ui.Option}s to the select button. + * @param {string} id Button ID; must equal a {@link goog.editor.Command} for + * built-in buttons, anything else for custom buttons. + * @param {string} tooltip Tooltip to be shown on hover. + * @param {goog.ui.ControlContent} caption Button caption; used as the + * default caption when nothing is selected. + * @param {string=} opt_classNames CSS class name(s) to apply to the button's + * root element. + * @param {goog.ui.MenuButtonRenderer=} opt_renderer Button renderer; + * defaults to {@link goog.ui.ToolbarMenuButtonRenderer} if unspecified. + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM + * creation; defaults to the current document if unspecified. + * @return {!goog.ui.Select} A select button. + */ +goog.ui.editor.ToolbarFactory.makeSelectButton = function( + id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper) { + var button = + new goog.ui.ToolbarSelect(null, null, opt_renderer, opt_domHelper); + if (opt_classNames) { + // Unlike the other button types, for goog.ui.Select buttons we apply the + // extra class names to the root element, because for select buttons the + // caption isn't stable (as it changes each time the selection changes). + goog.array.forEach( + opt_classNames.split(/\s+/), button.addClassName, button); + } + button.addClassName(goog.getCssName('goog-toolbar-select')); + button.setDefaultCaption(caption); + button.setId(id); + button.setTooltip(tooltip); + return button; +}; + + +/** + * Creates a color menu button with the given ID, tooltip, and caption. + * Applies any custom CSS class names to the button's caption element. The + * button is created with a default color menu containing standard color + * palettes. + * @param {string} id Button ID; must equal a {@link goog.editor.Command} for + * built-in toolbar buttons, but can be anything else for custom buttons. + * @param {string} tooltip Tooltip to be shown on hover. + * @param {goog.ui.ControlContent} caption Button caption. + * @param {string=} opt_classNames CSS class name(s) to apply to the caption + * element. + * @param {goog.ui.ColorMenuButtonRenderer=} opt_renderer Button renderer; + * defaults to {@link goog.ui.ToolbarColorMenuButtonRenderer} + * if unspecified. + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM + * creation; defaults to the current document if unspecified. + * @return {!goog.ui.ColorMenuButton} A color menu button. + */ +goog.ui.editor.ToolbarFactory.makeColorMenuButton = function( + id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper) { + var button = new goog.ui.ToolbarColorMenuButton( + goog.ui.editor.ToolbarFactory.createContent_( + caption, opt_classNames, opt_domHelper), + null, opt_renderer, opt_domHelper); + button.setId(id); + button.setTooltip(tooltip); + return button; +}; + + +/** + * Creates a new DIV that wraps a button caption, optionally applying CSS + * class names to it. Used as a helper function in button factory methods. + * @param {goog.ui.ControlContent} caption Button caption. + * @param {string=} opt_classNames CSS class name(s) to apply to the DIV that + * wraps the caption (if any). + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM + * creation; defaults to the current document if unspecified. + * @return {!Element} DIV that wraps the caption. + * @private + */ +goog.ui.editor.ToolbarFactory.createContent_ = function( + caption, opt_classNames, opt_domHelper) { + // FF2 doesn't like empty DIVs, especially when rendered right-to-left. + if ((!caption || caption == '') && goog.userAgent.GECKO && + !goog.userAgent.isVersionOrHigher('1.9a')) { + caption = goog.string.Unicode.NBSP; + } + return (opt_domHelper || goog.dom.getDomHelper()) + .createDom(goog.dom.TagName.DIV, opt_classNames, caption); +}; diff --git a/closure-library/closure/goog/ui/emoji/emoji.js b/closure-library/closure/goog/ui/emoji/emoji.js new file mode 100644 index 0000000000..a814558a55 --- /dev/null +++ b/closure-library/closure/goog/ui/emoji/emoji.js @@ -0,0 +1,137 @@ +// Copyright 2007 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 Emoji implementation. + * + */ + +goog.provide('goog.ui.emoji.Emoji'); + + + +/** + * Creates an emoji. + * + * A simple wrapper for an emoji. + * + * @param {string} url URL pointing to the source image for the emoji. + * @param {string} id The id of the emoji, e.g., 'std.1'. + * @param {number=} opt_height The height of the emoji, if undefined the + * natural height of the emoji is used. + * @param {number=} opt_width The width of the emoji, if undefined the natural + * width of the emoji is used. + * @param {string=} opt_altText The alt text for the emoji image, eg. the + * unicode character representation of the emoji. + * @constructor + * @final + */ +goog.ui.emoji.Emoji = function(url, id, opt_height, opt_width, opt_altText) { + /** + * The URL pointing to the source image for the emoji + * + * @type {string} + * @private + */ + this.url_ = url; + + /** + * The id of the emoji + * + * @type {string} + * @private + */ + this.id_ = id; + + /** + * The height of the emoji + * + * @type {?number} + * @private + */ + this.height_ = opt_height || null; + + /** + * The width of the emoji + * + * @type {?number} + * @private + */ + this.width_ = opt_width || null; + + /** + * The unicode of the emoji + * + * @type {?string} + * @private + */ + this.altText_ = opt_altText || null; +}; + + +/** + * The name of the goomoji attribute, used for emoji image elements. + * @type {string} + * @deprecated Use goog.ui.emoji.Emoji.DATA_ATTRIBUTE instead. + */ +goog.ui.emoji.Emoji.ATTRIBUTE = 'goomoji'; + + +/** + * The name of the goomoji data-attribute, used for emoji image elements. Data + * attributes are the preferred way in HTML5 to set custom attributes. + * @type {string} + */ +goog.ui.emoji.Emoji.DATA_ATTRIBUTE = 'data-' + goog.ui.emoji.Emoji.ATTRIBUTE; + + +/** + * @return {string} The URL for this emoji. + */ +goog.ui.emoji.Emoji.prototype.getUrl = function() { + return this.url_; +}; + + +/** + * @return {string} The id of this emoji. + */ +goog.ui.emoji.Emoji.prototype.getId = function() { + return this.id_; +}; + + +/** + * @return {?number} The height of this emoji. + */ +goog.ui.emoji.Emoji.prototype.getHeight = function() { + return this.height_; +}; + + +/** + * @return {?number} The width of this emoji. + */ +goog.ui.emoji.Emoji.prototype.getWidth = function() { + return this.width_; +}; + + +/** + * @return {?string} The alt text for the emoji image, eg. the unicode character + * representation of the emoji. + */ +goog.ui.emoji.Emoji.prototype.getAltText = function() { + return this.altText_; +}; diff --git a/closure-library/closure/goog/ui/emoji/emojipalette.js b/closure-library/closure/goog/ui/emoji/emojipalette.js new file mode 100644 index 0000000000..c09628c193 --- /dev/null +++ b/closure-library/closure/goog/ui/emoji/emojipalette.js @@ -0,0 +1,288 @@ +// Copyright 2007 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 Emoji Palette implementation. This provides a UI widget for + * choosing an emoji from a palette of possible choices. EmojiPalettes are + * contained within EmojiPickers. + * + * See ../demos/popupemojipicker.html for an example of how to instantiate + * an emoji picker. + * + * Based on goog.ui.ColorPicker (colorpicker.js). + * + */ + +goog.provide('goog.ui.emoji.EmojiPalette'); + +goog.require('goog.events.EventType'); +goog.require('goog.net.ImageLoader'); +goog.require('goog.ui.Palette'); +goog.require('goog.ui.emoji.Emoji'); +goog.require('goog.ui.emoji.EmojiPaletteRenderer'); + + + +/** + * A page of emoji to be displayed in an EmojiPicker. + * + * @param {Array>} emoji List of emoji for this page. + * @param {?string=} opt_urlPrefix Prefix that should be prepended to all URL. + * @param {goog.ui.PaletteRenderer=} opt_renderer Renderer used to render or + * decorate the palette; defaults to {@link goog.ui.PaletteRenderer}. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper. + * @extends {goog.ui.Palette} + * @constructor + * @final + */ +goog.ui.emoji.EmojiPalette = function( + emoji, opt_urlPrefix, opt_renderer, opt_domHelper) { + goog.ui.Palette.call( + this, null, opt_renderer || new goog.ui.emoji.EmojiPaletteRenderer(null), + opt_domHelper); + /** + * All the different emoji that this palette can display. Maps emoji ids + * (string) to the goog.ui.emoji.Emoji for that id. + * + * @type {Object} + * @private + */ + this.emojiCells_ = {}; + + /** + * Map of emoji id to index into this.emojiCells_. + * + * @type {Object} + * @private + */ + this.emojiMap_ = {}; + + /** + * List of the animated emoji in this palette. Each internal array is of type + * [HTMLDivElement, goog.ui.emoji.Emoji], and represents the palette item + * for that animated emoji, and the Emoji object. + * + * @type {Array>} + * @private + */ + this.animatedEmoji_ = []; + + this.urlPrefix_ = opt_urlPrefix || ''; + + /** + * Palette items that are displayed on this page of the emoji picker. Each + * item is a div wrapped around a div or an img. + * + * @type {Array} + * @private + */ + this.emoji_ = this.getEmojiArrayFromProperties_(emoji); + + this.setContent(this.emoji_); +}; +goog.inherits(goog.ui.emoji.EmojiPalette, goog.ui.Palette); + + +/** + * Indicates a prefix that should be prepended to all URLs of images in this + * emojipalette. This provides an optimization if the URLs are long, so that + * the client does not have to send a long string for each emoji. + * + * @type {string} + * @private + */ +goog.ui.emoji.EmojiPalette.prototype.urlPrefix_ = ''; + + +/** + * Whether the emoji images have been loaded. + * + * @type {boolean} + * @private + */ +goog.ui.emoji.EmojiPalette.prototype.imagesLoaded_ = false; + + +/** + * Image loader for loading animated emoji. + * + * @type {goog.net.ImageLoader} + * @private + */ +goog.ui.emoji.EmojiPalette.prototype.imageLoader_; + + +/** + * Helps create an array of emoji palette items from an array of emoji + * properties. Each element will be either a div with background-image set to + * a sprite, or an img element pointing directly to an emoji, and all elements + * are wrapped with an outer div for alignment issues (i.e., this allows + * centering the inner div). + * + * @param {Object} emojiGroup The group of emoji for this page. + * @return {!Array} The emoji items. + * @private + */ +goog.ui.emoji.EmojiPalette.prototype.getEmojiArrayFromProperties_ = function( + emojiGroup) { + var emojiItems = []; + + for (var i = 0; i < emojiGroup.length; i++) { + var url = emojiGroup[i][0]; + var id = emojiGroup[i][1]; + var spriteInfo = emojiGroup[i][2]; + var displayUrl = spriteInfo ? spriteInfo.getUrl() : this.urlPrefix_ + url; + + var item = this.getRenderer().createPaletteItem( + this.getDomHelper(), id, spriteInfo, displayUrl); + emojiItems.push(item); + + var emoji = new goog.ui.emoji.Emoji(url, id); + this.emojiCells_[id] = emoji; + this.emojiMap_[id] = i; + + // Keep track of sprited emoji that are animated, for later loading. + if (spriteInfo && spriteInfo.isAnimated()) { + this.animatedEmoji_.push([item, emoji]); + } + } + + // Create the image loader now so that tests can access it before it has + // started loading images. + if (this.animatedEmoji_.length > 0) { + this.imageLoader_ = new goog.net.ImageLoader(); + } + + this.imagesLoaded_ = true; + return emojiItems; +}; + + +/** + * Sends off requests for all the animated emoji and replaces their static + * sprites when the images are done downloading. + */ +goog.ui.emoji.EmojiPalette.prototype.loadAnimatedEmoji = function() { + if (this.animatedEmoji_.length > 0) { + for (var i = 0; i < this.animatedEmoji_.length; i++) { + var emoji = + /** @type {goog.ui.emoji.Emoji} */ (this.animatedEmoji_[i][1]); + var url = this.urlPrefix_ + emoji.getUrl(); + + this.imageLoader_.addImage(emoji.getId(), url); + } + + this.getHandler().listen( + this.imageLoader_, goog.events.EventType.LOAD, this.handleImageLoad_); + this.imageLoader_.start(); + } +}; + + +/** + * Handles image load events from the ImageLoader. + * + * @param {goog.events.Event} e The event object. + * @private + */ +goog.ui.emoji.EmojiPalette.prototype.handleImageLoad_ = function(e) { + var id = e.target.id; + var url = e.target.src; + // Just to be safe, we check to make sure we have an id and src url from + // the event target, which the ImageLoader sets to an Image object. + if (id && url) { + var item = this.emoji_[this.emojiMap_[id]]; + if (item) { + this.getRenderer().updateAnimatedPaletteItem(item, e.target); + } + } +}; + + +/** + * Returns the image loader that this palette uses. Used for testing. + * + * @return {goog.net.ImageLoader} the image loader. + */ +goog.ui.emoji.EmojiPalette.prototype.getImageLoader = function() { + return this.imageLoader_; +}; + + +/** @override */ +goog.ui.emoji.EmojiPalette.prototype.disposeInternal = function() { + goog.ui.emoji.EmojiPalette.superClass_.disposeInternal.call(this); + + if (this.imageLoader_) { + this.imageLoader_.dispose(); + this.imageLoader_ = null; + } + this.animatedEmoji_ = null; + this.emojiCells_ = null; + this.emojiMap_ = null; + this.emoji_ = null; +}; + + +/** + * Returns a goomoji id from an img or the containing td, or null if none + * exists for that element. + * + * @param {Element} el The element to get the Goomoji id from. + * @return {?string} A goomoji id from an img or the containing td, or null if + * none exists for that element. + * @private + */ +goog.ui.emoji.EmojiPalette.prototype.getGoomojiIdFromElement_ = function(el) { + if (!el) { + return null; + } + + var item = this.getRenderer().getContainingItem(this, el); + if (item) { + return item.getAttribute(goog.ui.emoji.Emoji.ATTRIBUTE) != '' ? + item.getAttribute(goog.ui.emoji.Emoji.ATTRIBUTE) : + item.getAttribute(goog.ui.emoji.Emoji.DATA_ATTRIBUTE); + } + return null; +}; + + +/** + * @return {goog.ui.emoji.Emoji} The currently selected emoji from this palette. + */ +goog.ui.emoji.EmojiPalette.prototype.getSelectedEmoji = function() { + var elem = /** @type {Element} */ (this.getSelectedItem()); + var goomojiId = this.getGoomojiIdFromElement_(elem); + return this.emojiCells_[goomojiId]; +}; + + +/** + * @return {number} The number of emoji managed by this palette. + */ +goog.ui.emoji.EmojiPalette.prototype.getNumberOfEmoji = function() { + return this.emojiCells_.length; +}; + + +/** + * Returns the index of the specified emoji within this palette. + * + * @param {string} id Id of the emoji to look up. + * @return {number} The index of the specified emoji within this palette. + */ +goog.ui.emoji.EmojiPalette.prototype.getEmojiIndex = function(id) { + return this.emojiMap_[id]; +}; diff --git a/closure-library/closure/goog/ui/emoji/emojipaletterenderer.js b/closure-library/closure/goog/ui/emoji/emojipaletterenderer.js new file mode 100644 index 0000000000..7e27b5568a --- /dev/null +++ b/closure-library/closure/goog/ui/emoji/emojipaletterenderer.js @@ -0,0 +1,218 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Emoji Palette renderer implementation. + * @suppress {checkPrototypalTypes} + * + */ + +goog.provide('goog.ui.emoji.EmojiPaletteRenderer'); + +goog.require('goog.a11y.aria'); +goog.require('goog.asserts'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.classlist'); +goog.require('goog.style'); +goog.require('goog.ui.PaletteRenderer'); +goog.require('goog.ui.emoji.Emoji'); + +goog.forwardDeclare('goog.ui.Palette'); +goog.forwardDeclare('goog.ui.emoji.SpriteInfo'); + + + +/** + * Renders an emoji palette. + * + * @param {?string} defaultImgUrl Url of the img that should be used to fill up + * the cells in the emoji table, to prevent jittering. Will be stretched + * to the emoji cell size. A good image is a transparent dot. + * @constructor + * @extends {goog.ui.PaletteRenderer} + */ +goog.ui.emoji.EmojiPaletteRenderer = function(defaultImgUrl) { + goog.ui.PaletteRenderer.call(this); + + this.defaultImgUrl_ = defaultImgUrl; +}; +goog.inherits(goog.ui.emoji.EmojiPaletteRenderer, goog.ui.PaletteRenderer); + + +/** + * Globally unique ID sequence for cells rendered by this renderer class. + * @type {number} + * @private + */ +goog.ui.emoji.EmojiPaletteRenderer.cellId_ = 0; + + +/** + * Url of the img that should be used for cells in the emoji palette that are + * not filled with emoji, i.e., after all the emoji have already been placed + * on a page. + * + * @type {?string} + * @private + */ +goog.ui.emoji.EmojiPaletteRenderer.prototype.defaultImgUrl_ = null; + + +/** @override */ +goog.ui.emoji.EmojiPaletteRenderer.getCssClass = function() { + return goog.getCssName('goog-ui-emojipalette'); +}; + + +/** + * Creates a palette item from the given emoji data. + * + * @param {goog.dom.DomHelper} dom DOM helper for constructing DOM elements. + * @param {string} id Goomoji id for the emoji. + * @param {goog.ui.emoji.SpriteInfo} spriteInfo Spriting info for the emoji. + * @param {string} displayUrl URL of the image served for this cell, whether + * an individual emoji image or a sprite. + * @return {!HTMLDivElement} The palette item for this emoji. + */ +goog.ui.emoji.EmojiPaletteRenderer.prototype.createPaletteItem = function( + dom, id, spriteInfo, displayUrl) { + var el; + + if (spriteInfo) { + var cssClass = spriteInfo.getCssClass(); + if (cssClass) { + el = dom.createDom(goog.dom.TagName.DIV, cssClass); + } else { + el = this.buildElementFromSpriteMetadata(dom, spriteInfo, displayUrl); + } + } else { + el = dom.createDom(goog.dom.TagName.IMG, {'src': displayUrl}); + } + + var outerdiv = dom.createDom( + goog.dom.TagName.DIV, goog.getCssName('goog-palette-cell-wrapper'), el); + outerdiv.setAttribute(goog.ui.emoji.Emoji.ATTRIBUTE, id); + outerdiv.setAttribute(goog.ui.emoji.Emoji.DATA_ATTRIBUTE, id); + return /** @type {!HTMLDivElement} */ (outerdiv); +}; + + +/** + * Modifies a palette item containing an animated emoji, in response to the + * animated emoji being successfully downloaded. + * + * @param {Element} item The palette item to update. + * @param {Image} animatedImg An Image object containing the animated emoji. + */ +goog.ui.emoji.EmojiPaletteRenderer.prototype.updateAnimatedPaletteItem = + function(item, animatedImg) { + // An animated emoji is one that had sprite info for a static version and is + // now being updated. See createPaletteItem for the structure of the palette + // items we're modifying. + + var inner = /** @type {Element} */ (item.firstChild); + goog.asserts.assert(inner); + // The first case is a palette item with a CSS class representing the sprite, + // and an animated emoji. + var classes = goog.dom.classlist.get(inner); + if (classes && classes.length == 1) { + inner.className = ''; + } + + goog.style.setStyle(inner, { + 'width': animatedImg.width, + 'height': animatedImg.height, + 'background-image': 'url(' + animatedImg.src + ')', + 'background-position': '0 0' + }); +}; + + +/** + * Builds the inner contents of a palette item out of sprite metadata. + * + * @param {goog.dom.DomHelper} dom DOM helper for constructing DOM elements. + * @param {goog.ui.emoji.SpriteInfo} spriteInfo The metadata to create the css + * for the sprite. + * @param {string} displayUrl The URL of the image for this cell. + * @return {HTMLDivElement} The inner element for a palette item. + */ +goog.ui.emoji.EmojiPaletteRenderer.prototype.buildElementFromSpriteMetadata = + function(dom, spriteInfo, displayUrl) { + var width = spriteInfo.getWidthCssValue(); + var height = spriteInfo.getHeightCssValue(); + var x = spriteInfo.getXOffsetCssValue(); + var y = spriteInfo.getYOffsetCssValue(); + + var el = dom.createDom(goog.dom.TagName.DIV); + goog.style.setStyle(el, { + 'width': width, + 'height': height, + 'background-image': 'url(' + displayUrl + ')', + 'background-repeat': 'no-repeat', + 'background-position': x + ' ' + y + }); + + return /** @type {!HTMLDivElement} */ (el); +}; + + +/** @override */ +goog.ui.emoji.EmojiPaletteRenderer.prototype.createCell = function(node, dom) { + // Create a cell with the default img if we're out of items, in order to + // prevent jitter in the table. If there's no default img url, just create an + // empty div, to prevent trying to fetch a null url. + if (!node) { + var elem = this.defaultImgUrl_ ? + dom.createDom(goog.dom.TagName.IMG, {src: this.defaultImgUrl_}) : + dom.createDom(goog.dom.TagName.DIV); + node = dom.createDom( + goog.dom.TagName.DIV, goog.getCssName('goog-palette-cell-wrapper'), + elem); + } + + var cell = dom.createDom( + goog.dom.TagName.TD, { + 'class': goog.getCssName(this.getCssClass(), 'cell'), + // Cells must have an ID, for accessibility, so we generate one here. + 'id': this.getCssClass() + '-cell-' + + goog.ui.emoji.EmojiPaletteRenderer.cellId_++ + }, + node); + goog.a11y.aria.setRole(cell, 'gridcell'); + return cell; +}; + + +/** + * Returns the item corresponding to the given node, or null if the node is + * neither a palette cell nor part of a palette item. + * @param {goog.ui.Palette} palette Palette in which to look for the item. + * @param {Node} node Node to look for. + * @return {Node} The corresponding palette item (null if not found). + * @override + */ +goog.ui.emoji.EmojiPaletteRenderer.prototype.getContainingItem = function( + palette, node) { + var root = palette.getElement(); + while (node && node.nodeType == goog.dom.NodeType.ELEMENT && node != root) { + if (node.tagName == goog.dom.TagName.TD) { + return node.firstChild; + } + node = node.parentNode; + } + + return null; +}; diff --git a/closure-library/closure/goog/ui/emoji/emojipicker.js b/closure-library/closure/goog/ui/emoji/emojipicker.js new file mode 100644 index 0000000000..c607c2d025 --- /dev/null +++ b/closure-library/closure/goog/ui/emoji/emojipicker.js @@ -0,0 +1,797 @@ +// Copyright 2007 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 Emoji Picker implementation. This provides a UI widget for + * choosing an emoji from a grid of possible choices. + * + * @see ../demos/popupemojipicker.html for an example of how to instantiate + * an emoji picker. + * + * Based on goog.ui.ColorPicker (colorpicker.js). + * + * @see ../../demos/popupemojipicker.html + */ + +goog.provide('goog.ui.emoji.EmojiPicker'); + +goog.require('goog.dom.TagName'); +goog.require('goog.style'); +goog.require('goog.ui.Component'); +goog.require('goog.ui.TabPane'); +goog.require('goog.ui.emoji.Emoji'); +goog.require('goog.ui.emoji.EmojiPalette'); +goog.require('goog.ui.emoji.EmojiPaletteRenderer'); +goog.require('goog.ui.emoji.ProgressiveEmojiPaletteRenderer'); + + + +/** + * Creates a new, empty emoji picker. An emoji picker is a grid of emoji, each + * cell of the grid containing a single emoji. The picker may contain multiple + * pages of emoji. + * + * When a user selects an emoji, by either clicking or pressing enter, the + * picker fires a goog.ui.Component.EventType.ACTION event with the id. The + * client listens on this event and in the handler can retrieve the id of the + * selected emoji and do something with it, for instance, inserting an image + * tag into a rich text control. An emoji picker does not maintain state. That + * is, once an emoji is selected, the emoji picker does not remember which emoji + * was selected. + * + * The emoji picker is implemented as a tabpane with each tabpage being a table. + * Each of the tables are the same size to prevent jittering when switching + * between pages. + * + * @param {string} defaultImgUrl Url of the img that should be used to fill up + * the cells in the emoji table, to prevent jittering. Should be the same + * size as the emoji. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper. + * @extends {goog.ui.Component} + * @constructor + */ +goog.ui.emoji.EmojiPicker = function(defaultImgUrl, opt_domHelper) { + goog.ui.Component.call(this, opt_domHelper); + + this.defaultImgUrl_ = defaultImgUrl; + + /** + * Emoji that this picker displays. + * + * @type {Array} + * @private + */ + this.emoji_ = []; + + /** + * Pages of this emoji picker. + * + * @type {Array} + * @private + */ + this.pages_ = []; + + /** + * Keeps track of which pages in the picker have been loaded. Used for delayed + * loading of tabs. + * + * @type {Array} + * @private + */ + this.pageLoadStatus_ = []; + + /** + * Tabpane to hold the pages of this emojipicker. + * + * @type {goog.ui.TabPane} + * @private + */ + this.tabPane_ = null; + + this.getHandler().listen( + this, goog.ui.Component.EventType.ACTION, this.onEmojiPaletteAction_); +}; +goog.inherits(goog.ui.emoji.EmojiPicker, goog.ui.Component); + + +/** + * Default number of rows per grid of emoji. + * + * @type {number} + */ +goog.ui.emoji.EmojiPicker.DEFAULT_NUM_ROWS = 5; + + +/** + * Default number of columns per grid of emoji. + * + * @type {number} + */ +goog.ui.emoji.EmojiPicker.DEFAULT_NUM_COLS = 10; + + +/** + * Default location of the tabs in relation to the emoji grids. + * + * @type {goog.ui.TabPane.TabLocation} + */ +goog.ui.emoji.EmojiPicker.DEFAULT_TAB_LOCATION = + goog.ui.TabPane.TabLocation.TOP; + + +/** @private {goog.ui.emoji.Emoji} */ +goog.ui.emoji.EmojiPicker.prototype.selectedEmoji_; + + +/** @private {goog.ui.emoji.EmojiPaletteRenderer} */ +goog.ui.emoji.EmojiPicker.prototype.renderer_; + + +/** + * Number of rows per grid of emoji. + * + * @type {number} + * @private + */ +goog.ui.emoji.EmojiPicker.prototype.numRows_ = + goog.ui.emoji.EmojiPicker.DEFAULT_NUM_ROWS; + + +/** + * Number of columns per grid of emoji. + * + * @type {number} + * @private + */ +goog.ui.emoji.EmojiPicker.prototype.numCols_ = + goog.ui.emoji.EmojiPicker.DEFAULT_NUM_COLS; + + +/** + * Whether the number of rows in the picker should be automatically determined + * by the specified number of columns so as to minimize/eliminate jitter when + * switching between tabs. + * + * @type {boolean} + * @private + */ +goog.ui.emoji.EmojiPicker.prototype.autoSizeByColumnCount_ = true; + + +/** + * Location of the tabs for the picker tabpane. + * + * @type {goog.ui.TabPane.TabLocation} + * @private + */ +goog.ui.emoji.EmojiPicker.prototype.tabLocation_ = + goog.ui.emoji.EmojiPicker.DEFAULT_TAB_LOCATION; + + +/** + * Whether the component is focusable. + * @type {boolean} + * @private + */ +goog.ui.emoji.EmojiPicker.prototype.focusable_ = true; + + +/** + * Url of the img that should be used for cells in the emoji picker that are + * not filled with emoji, i.e., after all the emoji have already been placed + * on a page. + * + * @type {string} + * @private + */ +goog.ui.emoji.EmojiPicker.prototype.defaultImgUrl_; + + +/** + * If present, indicates a prefix that should be prepended to all URLs + * of images in this emojipicker. This provides an optimization if the URLs + * are long, so that the client does not have to send a long string for each + * emoji. + * + * @type {string|undefined} + * @private + */ +goog.ui.emoji.EmojiPicker.prototype.urlPrefix_; + + +/** + * If true, delay loading the images for the emojipalettes until after + * construction. This gives a better user experience before the images are in + * the cache, since other widgets waiting for construction of the emojipalettes + * won't have to wait for all the images (which may be a substantial amount) to + * load. + * + * @type {boolean} + * @private + */ +goog.ui.emoji.EmojiPicker.prototype.delayedLoad_ = false; + + +/** + * Whether to use progressive rendering in the emojipicker's palette, if using + * sprited imgs. If true, then uses img tags, which most browsers render + * progressively (i.e., as the data comes in). If false, then uses div tags + * with the background-image, which some newer browsers render progressively + * but older ones do not. + * + * @type {boolean} + * @private + */ +goog.ui.emoji.EmojiPicker.prototype.progressiveRender_ = false; + + +/** + * Whether to require the caller to manually specify when to start loading + * animated emoji. This is primarily for unittests to be able to test the + * structure of the emojipicker palettes before and after the animated emoji + * have been loaded. + * + * @type {boolean} + * @private + */ +goog.ui.emoji.EmojiPicker.prototype.manualLoadOfAnimatedEmoji_ = false; + + +/** + * Index of the active page in the picker. + * + * @type {number} + * @private + */ +goog.ui.emoji.EmojiPicker.prototype.activePage_ = -1; + + +/** + * Adds a group of emoji to the picker. + * + * @param {string|Element} title Title for the group. + * @param {Array>} emojiGroup A new group of emoji to be added + * Each internal array contains [emojiUrl, emojiId]. + */ +goog.ui.emoji.EmojiPicker.prototype.addEmojiGroup = function( + title, emojiGroup) { + this.emoji_.push({title: title, emoji: emojiGroup}); +}; + + +/** + * Gets the number of rows per grid in the emoji picker. + * + * @return {number} number of rows per grid. + */ +goog.ui.emoji.EmojiPicker.prototype.getNumRows = function() { + return this.numRows_; +}; + + +/** + * Gets the number of columns per grid in the emoji picker. + * + * @return {number} number of columns per grid. + */ +goog.ui.emoji.EmojiPicker.prototype.getNumColumns = function() { + return this.numCols_; +}; + + +/** + * Sets the number of rows per grid in the emoji picker. This should only be + * called before the picker has been rendered. + * + * @param {number} numRows Number of rows per grid. + */ +goog.ui.emoji.EmojiPicker.prototype.setNumRows = function(numRows) { + this.numRows_ = numRows; +}; + + +/** + * Sets the number of columns per grid in the emoji picker. This should only be + * called before the picker has been rendered. + * + * @param {number} numCols Number of columns per grid. + */ +goog.ui.emoji.EmojiPicker.prototype.setNumColumns = function(numCols) { + this.numCols_ = numCols; +}; + + +/** + * Sets whether to automatically size the emojipicker based on the number of + * columns and the number of emoji in each group, so as to reduce jitter. + * + * @param {boolean} autoSize Whether to automatically size the picker. + */ +goog.ui.emoji.EmojiPicker.prototype.setAutoSizeByColumnCount = function( + autoSize) { + this.autoSizeByColumnCount_ = autoSize; +}; + + +/** + * Sets the location of the tabs in relation to the emoji grids. This should + * only be called before the picker has been rendered. + * + * @param {goog.ui.TabPane.TabLocation} tabLocation The location of the tabs. + */ +goog.ui.emoji.EmojiPicker.prototype.setTabLocation = function(tabLocation) { + this.tabLocation_ = tabLocation; +}; + + +/** + * Sets whether loading of images should be delayed until after dom creation. + * Thus, this function must be called before {@link #createDom}. If set to true, + * the client must call {@link #loadImages} when they wish the images to be + * loaded. + * + * @param {boolean} shouldDelay Whether to delay loading the images. + */ +goog.ui.emoji.EmojiPicker.prototype.setDelayedLoad = function(shouldDelay) { + this.delayedLoad_ = shouldDelay; +}; + + +/** + * Sets whether to require the caller to manually specify when to start loading + * animated emoji. This is primarily for unittests to be able to test the + * structure of the emojipicker palettes before and after the animated emoji + * have been loaded. This only affects sprited emojipickers with sprite data + * for animated emoji. + * + * @param {boolean} manual Whether to load animated emoji manually. + */ +goog.ui.emoji.EmojiPicker.prototype.setManualLoadOfAnimatedEmoji = function( + manual) { + this.manualLoadOfAnimatedEmoji_ = manual; +}; + + +/** + * Returns true if the component is focusable, false otherwise. The default + * is true. Focusable components always have a tab index and allocate a key + * handler to handle keyboard events while focused. + * @return {boolean} Whether the component is focusable. + */ +goog.ui.emoji.EmojiPicker.prototype.isFocusable = function() { + return this.focusable_; +}; + + +/** + * Sets whether the component is focusable. The default is true. + * Focusable components always have a tab index and allocate a key handler to + * handle keyboard events while focused. + * @param {boolean} focusable Whether the component is focusable. + */ +goog.ui.emoji.EmojiPicker.prototype.setFocusable = function(focusable) { + this.focusable_ = focusable; + for (var i = 0; i < this.pages_.length; i++) { + if (this.pages_[i]) { + this.pages_[i].setSupportedState( + goog.ui.Component.State.FOCUSED, focusable); + } + } +}; + + +/** + * Sets the URL prefix for the emoji URLs. + * + * @param {string} urlPrefix Prefix that should be prepended to all URLs. + */ +goog.ui.emoji.EmojiPicker.prototype.setUrlPrefix = function(urlPrefix) { + this.urlPrefix_ = urlPrefix; +}; + + +/** + * Sets the progressive rendering aspect of this emojipicker. Must be called + * before createDom to have an effect. + * + * @param {boolean} progressive Whether this picker should render progressively. + */ +goog.ui.emoji.EmojiPicker.prototype.setProgressiveRender = function( + progressive) { + this.progressiveRender_ = progressive; +}; + + +/** + * Adjusts the number of rows to be the maximum row count out of all the emoji + * groups, in order to prevent jitter in switching among the tabs. + * @private + * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.emoji.EmojiPicker.prototype.adjustNumRowsIfNecessary_ = function() { + var currentMax = 0; + + for (var i = 0; i < this.emoji_.length; i++) { + var numEmoji = this.emoji_[i].emoji.length; + var rowsNeeded = Math.ceil(numEmoji / this.numCols_); + if (rowsNeeded > currentMax) { + currentMax = rowsNeeded; + } + } + + this.setNumRows(currentMax); +}; + + +/** + * Causes the emoji imgs to be loaded into the picker. Used for delayed loading. + * No-op if delayed loading is not set. + */ +goog.ui.emoji.EmojiPicker.prototype.loadImages = function() { + if (!this.delayedLoad_) { + return; + } + + // Load the first page only + this.loadPage_(0); + this.activePage_ = 0; +}; + + +/** + * @override + * @suppress {deprecated,strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.emoji.EmojiPicker.prototype.createDom = function() { + this.setElementInternal(this.getDomHelper().createDom(goog.dom.TagName.DIV)); + + if (this.autoSizeByColumnCount_) { + this.adjustNumRowsIfNecessary_(); + } + + if (this.emoji_.length == 0) { + throw new Error('Must add some emoji to the picker'); + } + + // If there is more than one group of emoji, we construct a tabpane + if (this.emoji_.length > 1) { + // Give the tabpane a div to use as its content element, since tabpane + // overwrites the CSS class of the element it's passed + var div = this.getDomHelper().createDom(goog.dom.TagName.DIV); + this.getElement().appendChild(div); + this.tabPane_ = new goog.ui.TabPane( + div, this.tabLocation_, this.getDomHelper(), true /* use MOUSEDOWN */); + } + + this.renderer_ = this.progressiveRender_ ? + new goog.ui.emoji.ProgressiveEmojiPaletteRenderer(this.defaultImgUrl_) : + new goog.ui.emoji.EmojiPaletteRenderer(this.defaultImgUrl_); + + for (var i = 0; i < this.emoji_.length; i++) { + var emoji = this.emoji_[i].emoji; + var page = this.delayedLoad_ ? this.createPlaceholderEmojiPage_(emoji) : + this.createEmojiPage_(emoji, i); + this.pages_.push(page); + } + + this.activePage_ = 0; + this.getElement().tabIndex = 0; +}; + + +/** + * Used by unittests to manually load the animated emoji for this picker. + */ +goog.ui.emoji.EmojiPicker.prototype.manuallyLoadAnimatedEmoji = function() { + for (var i = 0; i < this.pages_.length; i++) { + this.pages_[i].loadAnimatedEmoji(); + } +}; + + +/** + * Creates a page if it has not already been loaded. This has the side effects + * of setting the load status of the page to true. + * + * @param {Array>} emoji Emoji for this page. See + * {@link addEmojiGroup} for more details. + * @param {number} index Index of the page in the emojipicker. + * @return {goog.ui.emoji.EmojiPalette} the emoji page. + * @private + */ +goog.ui.emoji.EmojiPicker.prototype.createEmojiPage_ = function(emoji, index) { + // Safeguard against trying to create the same page twice + if (this.pageLoadStatus_[index]) { + return null; + } + + var palette = new goog.ui.emoji.EmojiPalette( + emoji, this.urlPrefix_, this.renderer_, this.getDomHelper()); + if (!this.manualLoadOfAnimatedEmoji_) { + palette.loadAnimatedEmoji(); + } + palette.setSize(this.numCols_, this.numRows_); + palette.setSupportedState(goog.ui.Component.State.FOCUSED, this.focusable_); + palette.createDom(); + palette.setParent(this); + + this.pageLoadStatus_[index] = true; + + return palette; +}; + + +/** + * Returns an array of emoji whose real URLs have been replaced with the + * default img URL. Used for delayed loading. + * + * @param {Array>} emoji Original emoji array. + * @return {!Array>} emoji array with all emoji pointing to the + * default img. + * @private + */ +goog.ui.emoji.EmojiPicker.prototype.getPlaceholderEmoji_ = function(emoji) { + var placeholderEmoji = []; + + for (var i = 0; i < emoji.length; i++) { + placeholderEmoji.push([this.defaultImgUrl_, emoji[i][1]]); + } + + return placeholderEmoji; +}; + + +/** + * Creates an emoji page using placeholder emoji pointing to the default + * img instead of the real emoji. Used for delayed loading. + * + * @param {Array>} emoji Emoji for this page. See + * {@link addEmojiGroup} for more details. + * @return {!goog.ui.emoji.EmojiPalette} the emoji page. + * @private + */ +goog.ui.emoji.EmojiPicker.prototype.createPlaceholderEmojiPage_ = function( + emoji) { + var placeholderEmoji = this.getPlaceholderEmoji_(emoji); + + var palette = new goog.ui.emoji.EmojiPalette( + placeholderEmoji, + null, // no url prefix + this.renderer_, this.getDomHelper()); + palette.setSize(this.numCols_, this.numRows_); + palette.setSupportedState(goog.ui.Component.State.FOCUSED, this.focusable_); + palette.createDom(); + palette.setParent(this); + + return palette; +}; + + +/** + * EmojiPickers cannot be used to decorate pre-existing html, since the + * structure they build is fairly complicated. + * @param {Element} element Element to decorate. + * @return {boolean} Returns always false. + * @override + */ +goog.ui.emoji.EmojiPicker.prototype.canDecorate = function(element) { + return false; +}; + + +/** + * @override + * @suppress {deprecated,strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.emoji.EmojiPicker.prototype.enterDocument = function() { + goog.ui.emoji.EmojiPicker.superClass_.enterDocument.call(this); + + for (var i = 0; i < this.pages_.length; i++) { + this.pages_[i].enterDocument(); + var pageElement = this.pages_[i].getElement(); + + // Add a new tab to the tabpane if there's more than one group of emoji. + // If there is just one group of emoji, then we simply use the single + // page's element as the content for the picker + if (this.pages_.length > 1) { + // Create a simple default title containg the page number if the title + // was not provided in the emoji group params + var title = this.emoji_[i].title || (i + 1); + this.tabPane_.addPage( + new goog.ui.TabPane.TabPage(pageElement, title, this.getDomHelper())); + } else { + this.getElement().appendChild(pageElement); + } + } + + // Initialize listeners. Note that we need to initialize this listener + // after createDom, because addPage causes the goog.ui.TabPane.Events.CHANGE + // event to fire, but we only want the handler (which loads delayed images) + // to run after the picker has been constructed. + if (this.tabPane_) { + this.getHandler().listen( + this.tabPane_, goog.ui.TabPane.Events.CHANGE, this.onPageChanged_); + + // Make the tabpane unselectable so that changing tabs doesn't disturb the + // cursor + goog.style.setUnselectable(this.tabPane_.getElement(), true); + } + + this.getElement().unselectable = 'on'; +}; + + +/** @override */ +goog.ui.emoji.EmojiPicker.prototype.exitDocument = function() { + goog.ui.emoji.EmojiPicker.superClass_.exitDocument.call(this); + for (var i = 0; i < this.pages_.length; i++) { + this.pages_[i].exitDocument(); + } +}; + + +/** @override */ +goog.ui.emoji.EmojiPicker.prototype.disposeInternal = function() { + goog.ui.emoji.EmojiPicker.superClass_.disposeInternal.call(this); + + if (this.tabPane_) { + this.tabPane_.dispose(); + this.tabPane_ = null; + } + + for (var i = 0; i < this.pages_.length; i++) { + this.pages_[i].dispose(); + } + this.pages_.length = 0; +}; + + +/** + * @return {string} CSS class for the root element of EmojiPicker. + */ +goog.ui.emoji.EmojiPicker.prototype.getCssClass = function() { + return goog.getCssName('goog-ui-emojipicker'); +}; + + +/** + * Returns the currently selected emoji from this picker. If the picker is + * using the URL prefix optimization, allocates a new emoji object with the + * full URL. This method is meant to be used by clients of the emojipicker, + * e.g., in a listener on goog.ui.component.EventType.ACTION that wants to use + * the just-selected emoji. + * + * @return {goog.ui.emoji.Emoji} The currently selected emoji from this picker. + */ +goog.ui.emoji.EmojiPicker.prototype.getSelectedEmoji = function() { + return this.urlPrefix_ ? + new goog.ui.emoji.Emoji( + this.urlPrefix_ + this.selectedEmoji_.getId(), + this.selectedEmoji_.getId()) : + this.selectedEmoji_; +}; + + +/** + * Returns the number of emoji groups in this picker. + * + * @return {number} The number of emoji groups in this picker. + */ +goog.ui.emoji.EmojiPicker.prototype.getNumEmojiGroups = function() { + return this.emoji_.length; +}; + + +/** + * Returns a page from the picker. This should be considered protected, and is + * ONLY FOR TESTING. + * + * @param {number} index Index of the page to return. + * @return {goog.ui.emoji.EmojiPalette?} the page at the specified index or null + * if none exists. + */ +goog.ui.emoji.EmojiPicker.prototype.getPage = function(index) { + return this.pages_[index]; +}; + + +/** + * Returns all the pages from the picker. This should be considered protected, + * and is ONLY FOR TESTING. + * + * @return {Array?} the pages in the picker or + * null if none exist. + */ +goog.ui.emoji.EmojiPicker.prototype.getPages = function() { + return this.pages_; +}; + + +/** + * Returns the tabpane if this is a multipage picker. This should be considered + * protected, and is ONLY FOR TESTING. + * + * @return {goog.ui.TabPane} the tabpane if it is a multipage picker, + * or null if it does not exist or is a single page picker. + */ +goog.ui.emoji.EmojiPicker.prototype.getTabPane = function() { + return this.tabPane_; +}; + + +/** + * @return {goog.ui.emoji.EmojiPalette} The active page of the emoji picker. + * @private + */ +goog.ui.emoji.EmojiPicker.prototype.getActivePage_ = function() { + return this.pages_[this.activePage_]; +}; + + +/** + * Handles actions from the EmojiPalettes that this picker contains. + * + * @param {goog.ui.Component.EventType} e The event object. + * @private + */ +goog.ui.emoji.EmojiPicker.prototype.onEmojiPaletteAction_ = function(e) { + this.selectedEmoji_ = this.getActivePage_().getSelectedEmoji(); +}; + + +/** + * Handles changes in the active page in the tabpane. + * + * @param {goog.ui.TabPaneEvent} e The event object. + * @private + */ +goog.ui.emoji.EmojiPicker.prototype.onPageChanged_ = function(e) { + var index = /** @type {number} */ (e.page.getIndex()); + this.loadPage_(index); + this.activePage_ = index; +}; + + +/** + * Loads a page into the picker if it has not yet been loaded. + * @param {number} index Index of the page to load. + * @private + * @suppress {deprecated,strictMissingProperties} Part of the go/strict_warnings_migration + */ +goog.ui.emoji.EmojiPicker.prototype.loadPage_ = function(index) { + if (index < 0 || index > this.pages_.length) { + throw new Error('Index out of bounds'); + } + + if (!this.pageLoadStatus_[index]) { + var oldPage = this.pages_[index]; + this.pages_[index] = this.createEmojiPage_(this.emoji_[index].emoji, index); + this.pages_[index].enterDocument(); + var pageElement = this.pages_[index].getElement(); + if (this.pages_.length > 1) { + this.tabPane_.removePage(index); + var title = this.emoji_[index].title || (index + 1); + this.tabPane_.addPage( + new goog.ui.TabPane.TabPage(pageElement, title, this.getDomHelper()), + index); + this.tabPane_.setSelectedIndex(index); + } else { + var el = this.getElement(); + el.appendChild(pageElement); + } + if (oldPage) { + oldPage.dispose(); + } + } +}; diff --git a/closure-library/closure/goog/ui/emoji/popupemojipicker.js b/closure-library/closure/goog/ui/emoji/popupemojipicker.js new file mode 100644 index 0000000000..8c59aed9d4 --- /dev/null +++ b/closure-library/closure/goog/ui/emoji/popupemojipicker.js @@ -0,0 +1,412 @@ +// Copyright 2007 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 Popup Emoji Picker implementation. This provides a UI widget + * for choosing an emoji from a grid of possible choices. The widget is a popup, + * so it is suitable for a toolbar, for instance the TrogEdit toolbar. + * + * @see ../demos/popupemojipicker.html for an example of how to instantiate + * an emoji picker. + * + * See goog.ui.emoji.EmojiPicker in emojipicker.js for more details. + * + * Based on goog.ui.PopupColorPicker (popupcolorpicker.js). + * + * @see ../../demos/popupemojipicker.html + */ + +goog.provide('goog.ui.emoji.PopupEmojiPicker'); + +goog.require('goog.events.EventType'); +goog.require('goog.positioning.AnchoredPosition'); +goog.require('goog.positioning.Corner'); +goog.require('goog.ui.Component'); +goog.require('goog.ui.Popup'); +goog.require('goog.ui.emoji.EmojiPicker'); + + + +/** + * Constructs a popup emoji picker widget. + * + * @param {string} defaultImgUrl Url of the img that should be used to fill up + * the cells in the emoji table, to prevent jittering. Should be the same + * size as the emoji. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper. + * @extends {goog.ui.Component} + * @constructor + * @final + */ +goog.ui.emoji.PopupEmojiPicker = function(defaultImgUrl, opt_domHelper) { + goog.ui.Component.call(this, opt_domHelper); + + this.emojiPicker_ = + new goog.ui.emoji.EmojiPicker(defaultImgUrl, opt_domHelper); + this.addChild(this.emojiPicker_); + + this.getHandler().listen( + this.emojiPicker_, goog.ui.Component.EventType.ACTION, + this.onEmojiPicked_); +}; +goog.inherits(goog.ui.emoji.PopupEmojiPicker, goog.ui.Component); + + +/** + * Instance of an emoji picker control. + * @type {goog.ui.emoji.EmojiPicker} + * @private + */ +goog.ui.emoji.PopupEmojiPicker.prototype.emojiPicker_ = null; + + +/** + * Instance of goog.ui.Popup used to manage the behavior of the emoji picker. + * @type {goog.ui.Popup} + * @private + */ +goog.ui.emoji.PopupEmojiPicker.prototype.popup_ = null; + + +/** + * Reference to the element that triggered the last popup. + * @type {Element} + * @private + */ +goog.ui.emoji.PopupEmojiPicker.prototype.lastTarget_ = null; + + +/** + * Whether the emoji picker can accept focus. + * @type {boolean} + * @private + */ +goog.ui.emoji.PopupEmojiPicker.prototype.focusable_ = true; + + +/** + * If true, then the emojipicker will toggle off if it is already visible. + * Default is true. + * @type {boolean} + * @private + */ +goog.ui.emoji.PopupEmojiPicker.prototype.toggleMode_ = true; + + +/** + * Adds a group of emoji to the picker. + * + * @param {string|Element} title Title for the group. + * @param {Array>} emojiGroup A new group of emoji to be added. Each + * internal array contains [emojiUrl, emojiId]. + */ +goog.ui.emoji.PopupEmojiPicker.prototype.addEmojiGroup = function( + title, emojiGroup) { + this.emojiPicker_.addEmojiGroup(title, emojiGroup); +}; + + +/** + * Sets whether the emoji picker should toggle if it is already open. + * @param {boolean} toggle The toggle mode to use. + */ +goog.ui.emoji.PopupEmojiPicker.prototype.setToggleMode = function(toggle) { + this.toggleMode_ = toggle; +}; + + +/** + * Gets whether the emojipicker is in toggle mode + * @return {boolean} toggle. + */ +goog.ui.emoji.PopupEmojiPicker.prototype.getToggleMode = function() { + return this.toggleMode_; +}; + + +/** + * Sets whether loading of images should be delayed until after dom creation. + * Thus, this function must be called before {@link #createDom}. If set to true, + * the client must call {@link #loadImages} when they wish the images to be + * loaded. + * + * @param {boolean} shouldDelay Whether to delay loading the images. + */ +goog.ui.emoji.PopupEmojiPicker.prototype.setDelayedLoad = function( + shouldDelay) { + if (this.emojiPicker_) { + this.emojiPicker_.setDelayedLoad(shouldDelay); + } +}; + + +/** + * Sets whether the emoji picker can accept focus. + * @param {boolean} focusable Whether the emoji picker should accept focus. + */ +goog.ui.emoji.PopupEmojiPicker.prototype.setFocusable = function(focusable) { + this.focusable_ = focusable; + if (this.emojiPicker_) { + // TODO(user): In next revision sort the behavior of passing state to + // children correctly + this.emojiPicker_.setFocusable(focusable); + } +}; + + +/** + * Sets the URL prefix for the emoji URLs. + * + * @param {string} urlPrefix Prefix that should be prepended to all URLs. + */ +goog.ui.emoji.PopupEmojiPicker.prototype.setUrlPrefix = function(urlPrefix) { + this.emojiPicker_.setUrlPrefix(urlPrefix); +}; + + +/** + * Sets the location of the tabs in relation to the emoji grids. This should + * only be called before the picker has been rendered. + * + * @param {goog.ui.TabPane.TabLocation} tabLocation The location of the tabs. + */ +goog.ui.emoji.PopupEmojiPicker.prototype.setTabLocation = function( + tabLocation) { + this.emojiPicker_.setTabLocation(tabLocation); +}; + + +/** + * Sets the number of rows per grid in the emoji picker. This should only be + * called before the picker has been rendered. + * + * @param {number} numRows Number of rows per grid. + */ +goog.ui.emoji.PopupEmojiPicker.prototype.setNumRows = function(numRows) { + this.emojiPicker_.setNumRows(numRows); +}; + + +/** + * Sets the number of columns per grid in the emoji picker. This should only be + * called before the picker has been rendered. + * + * @param {number} numCols Number of columns per grid. + */ +goog.ui.emoji.PopupEmojiPicker.prototype.setNumColumns = function(numCols) { + this.emojiPicker_.setNumColumns(numCols); +}; + + +/** + * Sets the progressive rendering aspect of this emojipicker. Must be called + * before createDom to have an effect. + * + * @param {boolean} progressive Whether the picker should render progressively. + */ +goog.ui.emoji.PopupEmojiPicker.prototype.setProgressiveRender = function( + progressive) { + if (this.emojiPicker_) { + this.emojiPicker_.setProgressiveRender(progressive); + } +}; + + +/** + * Returns the number of emoji groups in this picker. + * + * @return {number} The number of emoji groups in this picker. + */ +goog.ui.emoji.PopupEmojiPicker.prototype.getNumEmojiGroups = function() { + return this.emojiPicker_.getNumEmojiGroups(); +}; + + +/** + * Causes the emoji imgs to be loaded into the picker. Used for delayed loading. + */ +goog.ui.emoji.PopupEmojiPicker.prototype.loadImages = function() { + if (this.emojiPicker_) { + this.emojiPicker_.loadImages(); + } +}; + + +/** @override */ +goog.ui.emoji.PopupEmojiPicker.prototype.createDom = function() { + goog.ui.emoji.PopupEmojiPicker.superClass_.createDom.call(this); + + this.emojiPicker_.createDom(); + + this.getElement().className = goog.getCssName('goog-ui-popupemojipicker'); + this.getElement().appendChild(this.emojiPicker_.getElement()); + + this.popup_ = new goog.ui.Popup(this.getElement()); + this.getElement().unselectable = 'on'; +}; + + +/** @override */ +goog.ui.emoji.PopupEmojiPicker.prototype.disposeInternal = function() { + goog.ui.emoji.PopupEmojiPicker.superClass_.disposeInternal.call(this); + this.emojiPicker_ = null; + this.lastTarget_ = null; + if (this.popup_) { + this.popup_.dispose(); + this.popup_ = null; + } +}; + + +/** + * Attaches the popup emoji picker to an element. + * + * @param {Element} element The element to attach to. + */ +goog.ui.emoji.PopupEmojiPicker.prototype.attach = function(element) { + // TODO(user): standardize event type, popups should use MOUSEDOWN, but + // currently apps are using click. + this.getHandler().listen(element, goog.events.EventType.CLICK, this.show_); +}; + + +/** + * Detatches the popup emoji picker from an element. + * + * @param {Element} element The element to detach from. + */ +goog.ui.emoji.PopupEmojiPicker.prototype.detach = function(element) { + this.getHandler().unlisten(element, goog.events.EventType.CLICK, this.show_); +}; + + +/** + * @return {goog.ui.emoji.EmojiPicker} The emoji picker instance. + */ +goog.ui.emoji.PopupEmojiPicker.prototype.getEmojiPicker = function() { + return this.emojiPicker_; +}; + + +/** + * Returns whether the Popup dismisses itself when the user clicks outside of + * it. + * @return {boolean} Whether the Popup autohides on an external click. + */ +goog.ui.emoji.PopupEmojiPicker.prototype.getAutoHide = function() { + return !!this.popup_ && this.popup_.getAutoHide(); +}; + + +/** + * Sets whether the Popup dismisses itself when the user clicks outside of it - + * must be called after the Popup has been created (in createDom()), + * otherwise it does nothing. + * + * @param {boolean} autoHide Whether to autohide on an external click. + */ +goog.ui.emoji.PopupEmojiPicker.prototype.setAutoHide = function(autoHide) { + if (this.popup_) { + this.popup_.setAutoHide(autoHide); + } +}; + + +/** + * Returns the region inside which the Popup dismisses itself when the user + * clicks, or null if it was not set. Null indicates the entire document is + * the autohide region. + * @return {Element} The DOM element for autohide, or null if it hasn't been + * set. + */ +goog.ui.emoji.PopupEmojiPicker.prototype.getAutoHideRegion = function() { + return this.popup_ && this.popup_.getAutoHideRegion(); +}; + + +/** + * Sets the region inside which the Popup dismisses itself when the user + * clicks - must be called after the Popup has been created (in createDom()), + * otherwise it does nothing. + * + * @param {Element} element The DOM element for autohide. + */ +goog.ui.emoji.PopupEmojiPicker.prototype.setAutoHideRegion = function(element) { + if (this.popup_) { + this.popup_.setAutoHideRegion(element); + } +}; + + +/** + * Returns the {@link goog.ui.PopupBase} from this picker. Returns null if the + * popup has not yet been created. + * + * NOTE: This should *ONLY* be called from tests. If called before createDom(), + * this should return null. + * + * @return {goog.ui.PopupBase?} The popup, or null if it hasn't been created. + */ +goog.ui.emoji.PopupEmojiPicker.prototype.getPopup = function() { + return this.popup_; +}; + + +/** + * @return {Element} The last element that triggered the popup. + */ +goog.ui.emoji.PopupEmojiPicker.prototype.getLastTarget = function() { + return this.lastTarget_; +}; + + +/** + * @return {goog.ui.emoji.Emoji} The currently selected emoji. + */ +goog.ui.emoji.PopupEmojiPicker.prototype.getSelectedEmoji = function() { + return this.emojiPicker_.getSelectedEmoji(); +}; + + +/** + * Handles click events on the element this picker is attached to and shows the + * emoji picker in a popup. + * + * @param {goog.events.BrowserEvent} e The browser event. + * @private + */ +goog.ui.emoji.PopupEmojiPicker.prototype.show_ = function(e) { + if (this.popup_.isOrWasRecentlyVisible() && this.toggleMode_ && + this.lastTarget_ == e.currentTarget) { + this.popup_.setVisible(false); + return; + } + + this.lastTarget_ = /** @type {Element} */ (e.currentTarget); + this.popup_.setPosition( + new goog.positioning.AnchoredPosition( + this.lastTarget_, goog.positioning.Corner.BOTTOM_LEFT)); + this.popup_.setVisible(true); +}; + + +/** + * Handles selection of an emoji. + * + * @param {goog.events.Event} e The event object. + * @private + */ +goog.ui.emoji.PopupEmojiPicker.prototype.onEmojiPicked_ = function(e) { + this.popup_.setVisible(false); +}; diff --git a/closure-library/closure/goog/ui/emoji/progressiveemojipaletterenderer.js b/closure-library/closure/goog/ui/emoji/progressiveemojipaletterenderer.js new file mode 100644 index 0000000000..df3006fe45 --- /dev/null +++ b/closure-library/closure/goog/ui/emoji/progressiveemojipaletterenderer.js @@ -0,0 +1,95 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Progressive Emoji Palette renderer implementation. + * + */ + +goog.provide('goog.ui.emoji.ProgressiveEmojiPaletteRenderer'); + +goog.require('goog.dom.TagName'); +goog.require('goog.style'); +goog.require('goog.ui.emoji.EmojiPaletteRenderer'); + + + +/** + * Progressively renders an emoji palette. The progressive renderer tries to + * use img tags instead of background-image for sprited emoji, since most + * browsers render img tags progressively (i.e., as the data comes in), while + * only very new browsers render background-image progressively. + * + * @param {string} defaultImgUrl Url of the img that should be used to fill up + * the cells in the emoji table, to prevent jittering. Will be stretched + * to the emoji cell size. A good image is a transparent dot. + * @constructor + * @extends {goog.ui.emoji.EmojiPaletteRenderer} + * @final + */ +goog.ui.emoji.ProgressiveEmojiPaletteRenderer = function(defaultImgUrl) { + goog.ui.emoji.EmojiPaletteRenderer.call(this, defaultImgUrl); +}; +goog.inherits( + goog.ui.emoji.ProgressiveEmojiPaletteRenderer, + goog.ui.emoji.EmojiPaletteRenderer); + + +/** @override */ +goog.ui.emoji.ProgressiveEmojiPaletteRenderer.prototype + .buildElementFromSpriteMetadata = function(dom, spriteInfo, displayUrl) { + var width = spriteInfo.getWidthCssValue(); + var height = spriteInfo.getHeightCssValue(); + var x = spriteInfo.getXOffsetCssValue(); + var y = spriteInfo.getYOffsetCssValue(); + // Need this extra div for proper vertical centering. + var inner = dom.createDom(goog.dom.TagName.IMG, {'src': displayUrl}); + var el = dom.createDom( + goog.dom.TagName.DIV, goog.getCssName('goog-palette-cell-extra'), inner); + goog.style.setStyle(el, { + 'width': width, + 'height': height, + 'overflow': 'hidden', + 'position': 'relative' + }); + goog.style.setStyle(inner, {'left': x, 'top': y, 'position': 'absolute'}); + + return el; +}; + + +/** @override */ +goog.ui.emoji.ProgressiveEmojiPaletteRenderer.prototype + .updateAnimatedPaletteItem = function(item, animatedImg) { + // Just to be safe, we check for the existence of the img element within this + // palette item before attempting to modify it. + /** @type {!HTMLImageElement|undefined} */ + var img; + var el = item.firstChild; + while (el) { + if ('IMG' == /** @type {!Element} */ (el).tagName) { + img = /** @type {!HTMLImageElement} */ (el); + break; + } + el = el.firstChild; + } + if (!img) { + return; + } + + img.width = animatedImg.width; + img.height = animatedImg.height; + goog.style.setStyle(img, {'left': 0, 'top': 0}); + img.src = animatedImg.src; +}; diff --git a/closure-library/closure/goog/ui/emoji/spriteinfo.js b/closure-library/closure/goog/ui/emoji/spriteinfo.js new file mode 100644 index 0000000000..aa3d708d6f --- /dev/null +++ b/closure-library/closure/goog/ui/emoji/spriteinfo.js @@ -0,0 +1,214 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview SpriteInfo implementation. This is a simple wrapper class to + * hold CSS metadata needed for sprited emoji. + * + * @see ../demos/popupemojipicker.html or emojipicker_test.html for examples + * of how to use this class. + * + */ +goog.provide('goog.ui.emoji.SpriteInfo'); + + + +/** + * Creates a SpriteInfo object with the specified properties. If the image is + * sprited via CSS, then only the first parameter needs a value. If the image + * is sprited via metadata, then the first parameter should be left null. + * + * @param {?string} cssClass CSS class to properly display the sprited image. + * @param {string=} opt_url Url of the sprite image. + * @param {number=} opt_width Width of the image being sprited. + * @param {number=} opt_height Height of the image being sprited. + * @param {number=} opt_xOffset Positive x offset of the image being sprited + * within the sprite. + * @param {number=} opt_yOffset Positive y offset of the image being sprited + * within the sprite. + * @param {boolean=} opt_animated Whether the sprite is animated. + * @constructor + * @final + */ +goog.ui.emoji.SpriteInfo = function( + cssClass, opt_url, opt_width, opt_height, opt_xOffset, opt_yOffset, + opt_animated) { + if (cssClass != null) { + this.cssClass_ = cssClass; + } else { + if (opt_url == undefined || opt_width === undefined || + opt_height === undefined || opt_xOffset == undefined || + opt_yOffset === undefined) { + throw new Error('Sprite info is not fully specified'); + } + + this.url_ = opt_url; + this.width_ = opt_width; + this.height_ = opt_height; + this.xOffset_ = opt_xOffset; + this.yOffset_ = opt_yOffset; + } + + this.animated_ = !!opt_animated; +}; + + +/** + * Name of the CSS class to properly display the sprited image. + * @type {string} + * @private + */ +goog.ui.emoji.SpriteInfo.prototype.cssClass_; + + +/** + * Url of the sprite image. + * @type {string|undefined} + * @private + */ +goog.ui.emoji.SpriteInfo.prototype.url_; + + +/** + * Width of the image being sprited. + * @type {number|undefined} + * @private + */ +goog.ui.emoji.SpriteInfo.prototype.width_; + + +/** + * Height of the image being sprited. + * @type {number|undefined} + * @private + */ +goog.ui.emoji.SpriteInfo.prototype.height_; + + +/** + * Positive x offset of the image being sprited within the sprite. + * @type {number|undefined} + * @private + */ +goog.ui.emoji.SpriteInfo.prototype.xOffset_; + + +/** + * Positive y offset of the image being sprited within the sprite. + * @type {number|undefined} + * @private + */ +goog.ui.emoji.SpriteInfo.prototype.yOffset_; + + +/** + * Whether the emoji specified by the sprite is animated. + * @type {boolean} + * @private + */ +goog.ui.emoji.SpriteInfo.prototype.animated_; + + +/** + * Returns the css class of the sprited image. + * @return {?string} Name of the CSS class to properly display the sprited + * image. + */ +goog.ui.emoji.SpriteInfo.prototype.getCssClass = function() { + return this.cssClass_ || null; +}; + + +/** + * Returns the url of the sprite image. + * @return {?string} Url of the sprite image. + */ +goog.ui.emoji.SpriteInfo.prototype.getUrl = function() { + return this.url_ || null; +}; + + +/** + * Returns whether the emoji specified by this sprite is animated. + * @return {boolean} Whether the emoji is animated. + */ +goog.ui.emoji.SpriteInfo.prototype.isAnimated = function() { + return this.animated_; +}; + + +/** + * Returns the width of the image being sprited, appropriate for a CSS value. + * @return {string} The width of the image being sprited. + */ +goog.ui.emoji.SpriteInfo.prototype.getWidthCssValue = function() { + return goog.ui.emoji.SpriteInfo.getCssPixelValue_(this.width_); +}; + + +/** + * Returns the height of the image being sprited, appropriate for a CSS value. + * @return {string} The height of the image being sprited. + */ +goog.ui.emoji.SpriteInfo.prototype.getHeightCssValue = function() { + return goog.ui.emoji.SpriteInfo.getCssPixelValue_(this.height_); +}; + + +/** + * Returns the x offset of the image being sprited within the sprite, + * appropriate for a CSS value. + * @return {string} The x offset of the image being sprited within the sprite. + */ +goog.ui.emoji.SpriteInfo.prototype.getXOffsetCssValue = function() { + return goog.ui.emoji.SpriteInfo.getOffsetCssValue_(this.xOffset_); +}; + + +/** + * Returns the positive y offset of the image being sprited within the sprite, + * appropriate for a CSS value. + * @return {string} The y offset of the image being sprited within the sprite. + */ +goog.ui.emoji.SpriteInfo.prototype.getYOffsetCssValue = function() { + return goog.ui.emoji.SpriteInfo.getOffsetCssValue_(this.yOffset_); +}; + + +/** + * Returns a string appropriate for use as a CSS value. If the value is zero, + * then there is no unit appended. + * + * @param {number|undefined} value A number to be turned into a + * CSS size/location value. + * @return {string} A string appropriate for use as a CSS value. + * @private + */ +goog.ui.emoji.SpriteInfo.getCssPixelValue_ = function(value) { + return !value ? '0' : value + 'px'; +}; + + +/** + * Returns a string appropriate for use as a CSS value for a position offset, + * such as the position argument for sprites. + * + * @param {number|undefined} posOffset A positive offset for a position. + * @return {string} A string appropriate for use as a CSS value. + * @private + */ +goog.ui.emoji.SpriteInfo.getOffsetCssValue_ = function(posOffset) { + var offset = goog.ui.emoji.SpriteInfo.getCssPixelValue_(posOffset); + return offset == '0' ? offset : '-' + offset; +}; diff --git a/closure-library/closure/goog/ui/filteredmenu.js b/closure-library/closure/goog/ui/filteredmenu.js new file mode 100644 index 0000000000..279f5e47db --- /dev/null +++ b/closure-library/closure/goog/ui/filteredmenu.js @@ -0,0 +1,643 @@ +// Copyright 2007 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 Menu where items can be filtered based on user keyboard input. + * If a filter is specified only the items matching it will be displayed. + * + * @author eae@google.com (Emil A Eklund) + * @see ../demos/filteredmenu.html + */ + + +goog.provide('goog.ui.FilteredMenu'); + +goog.require('goog.a11y.aria'); +goog.require('goog.a11y.aria.AutoCompleteValues'); +goog.require('goog.a11y.aria.State'); +goog.require('goog.dom'); +goog.require('goog.dom.InputType'); +goog.require('goog.dom.TagName'); +goog.require('goog.events'); +goog.require('goog.events.EventType'); +goog.require('goog.events.InputHandler'); +goog.require('goog.events.KeyCodes'); +goog.require('goog.string'); +goog.require('goog.style'); +goog.require('goog.ui.Component'); +goog.require('goog.ui.FilterObservingMenuItem'); +goog.require('goog.ui.Menu'); +goog.require('goog.ui.MenuItem'); +goog.require('goog.userAgent'); + + + +/** + * Filtered menu class. + * @param {goog.ui.MenuRenderer=} opt_renderer Renderer used to render filtered + * menu; defaults to {@link goog.ui.MenuRenderer}. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper. + * @constructor + * @extends {goog.ui.Menu} + */ +goog.ui.FilteredMenu = function(opt_renderer, opt_domHelper) { + goog.ui.Menu.call(this, opt_domHelper, opt_renderer); +}; +goog.inherits(goog.ui.FilteredMenu, goog.ui.Menu); +goog.tagUnsealableClass(goog.ui.FilteredMenu); + + +/** + * Events fired by component. + * @enum {string} + */ +goog.ui.FilteredMenu.EventType = { + /** Dispatched after the component filter criteria has been changed. */ + FILTER_CHANGED: 'filterchange' +}; + + +/** + * Filter menu element ids. + * @enum {string} + * @private + */ +goog.ui.FilteredMenu.Id_ = { + CONTENT_ELEMENT: 'content-el' +}; + + +/** + * Filter input element. + * @type {Element|undefined} + * @private + */ +goog.ui.FilteredMenu.prototype.filterInput_; + + +/** + * The input handler that provides the input event. + * @type {goog.events.InputHandler|undefined} + * @private + */ +goog.ui.FilteredMenu.prototype.inputHandler_; + + +/** + * Maximum number of characters for filter input. + * @type {number} + * @private + */ +goog.ui.FilteredMenu.prototype.maxLength_ = 0; + + +/** + * Label displayed in the filter input when no text has been entered. + * @type {string} + * @private + */ +goog.ui.FilteredMenu.prototype.label_ = ''; + + +/** + * Label element. + * @type {Element|undefined} + * @private + */ +goog.ui.FilteredMenu.prototype.labelEl_; + + +/** + * Whether multiple items can be entered comma separated. + * @type {boolean} + * @private + */ +goog.ui.FilteredMenu.prototype.allowMultiple_ = false; + + +/** + * List of items entered in the search box if multiple entries are allowed. + * @type {Array|undefined} + * @private + */ +goog.ui.FilteredMenu.prototype.enteredItems_; + + +/** + * Index of first item that should be affected by the filter. Menu items with + * a lower index will not be affected by the filter. + * @type {number} + * @private + */ +goog.ui.FilteredMenu.prototype.filterFromIndex_ = 0; + + +/** + * Filter applied to the menu. + * @type {string|undefined|null} + * @private + */ +goog.ui.FilteredMenu.prototype.filterStr_; + + +/** + * @private {Element} + */ +goog.ui.FilteredMenu.prototype.contentElement_; + + +/** + * Map of child nodes that shouldn't be affected by filtering. + * @type {Object|undefined} + * @private + */ +goog.ui.FilteredMenu.prototype.persistentChildren_; + + +/** @override */ +goog.ui.FilteredMenu.prototype.createDom = function() { + goog.ui.FilteredMenu.superClass_.createDom.call(this); + + var dom = this.getDomHelper(); + var el = dom.createDom( + goog.dom.TagName.DIV, + goog.getCssName(this.getRenderer().getCssClass(), 'filter'), + this.labelEl_ = dom.createDom(goog.dom.TagName.DIV, null, this.label_), + this.filterInput_ = dom.createDom( + goog.dom.TagName.INPUT, {'type': goog.dom.InputType.TEXT})); + var element = this.getElement(); + dom.appendChild(element, el); + var contentElementId = this.makeId(goog.ui.FilteredMenu.Id_.CONTENT_ELEMENT); + this.contentElement_ = dom.createDom(goog.dom.TagName.DIV, { + 'class': goog.getCssName(this.getRenderer().getCssClass(), 'content'), + 'id': contentElementId + }); + dom.appendChild(element, this.contentElement_); + + this.initFilterInput_(); + + goog.a11y.aria.setState( + this.filterInput_, goog.a11y.aria.State.AUTOCOMPLETE, + goog.a11y.aria.AutoCompleteValues.LIST); + goog.a11y.aria.setState( + this.filterInput_, goog.a11y.aria.State.OWNS, contentElementId); + goog.a11y.aria.setState( + this.filterInput_, goog.a11y.aria.State.EXPANDED, true); +}; + + +/** + * Helper method that initializes the filter input element. + * @private + */ +goog.ui.FilteredMenu.prototype.initFilterInput_ = function() { + this.setFocusable(true); + this.setKeyEventTarget(this.filterInput_); + + // Workaround for mozilla bug #236791. + if (goog.userAgent.GECKO) { + this.filterInput_.setAttribute('autocomplete', 'off'); + } + + if (this.maxLength_) { + this.filterInput_.maxLength = this.maxLength_; + } +}; + + +/** + * Sets up listeners and prepares the filter functionality. + * @private + */ +goog.ui.FilteredMenu.prototype.setUpFilterListeners_ = function() { + if (!this.inputHandler_ && this.filterInput_) { + this.inputHandler_ = new goog.events.InputHandler( + /** @type {Element} */ (this.filterInput_)); + goog.style.setUnselectable(this.filterInput_, false); + goog.events.listen( + this.inputHandler_, goog.events.InputHandler.EventType.INPUT, + this.handleFilterEvent, false, this); + goog.events.listen( + this.filterInput_.parentNode, goog.events.EventType.CLICK, + this.onFilterLabelClick_, false, this); + if (this.allowMultiple_) { + this.enteredItems_ = []; + } + } +}; + + +/** + * Tears down listeners and resets the filter functionality. + * @private + */ +goog.ui.FilteredMenu.prototype.tearDownFilterListeners_ = function() { + if (this.inputHandler_) { + goog.events.unlisten( + this.inputHandler_, goog.events.InputHandler.EventType.INPUT, + this.handleFilterEvent, false, this); + goog.events.unlisten( + this.filterInput_.parentNode, goog.events.EventType.CLICK, + this.onFilterLabelClick_, false, this); + + this.inputHandler_.dispose(); + this.inputHandler_ = undefined; + this.enteredItems_ = undefined; + } +}; + + +/** @override */ +goog.ui.FilteredMenu.prototype.setVisible = function(show, opt_force, opt_e) { + var visibilityChanged = goog.ui.FilteredMenu.superClass_.setVisible.call( + this, show, opt_force, opt_e); + if (visibilityChanged && show && this.isInDocument()) { + this.setFilter(''); + this.setUpFilterListeners_(); + } else if (visibilityChanged && !show) { + this.tearDownFilterListeners_(); + } + + return visibilityChanged; +}; + + +/** @override */ +goog.ui.FilteredMenu.prototype.disposeInternal = function() { + this.tearDownFilterListeners_(); + this.filterInput_ = undefined; + this.labelEl_ = undefined; + goog.ui.FilteredMenu.superClass_.disposeInternal.call(this); +}; + + +/** + * Sets the filter label (the label displayed in the filter input element if no + * text has been entered). + * @param {?string} label Label text. + */ +goog.ui.FilteredMenu.prototype.setFilterLabel = function(label) { + this.label_ = label || ''; + if (this.labelEl_) { + goog.dom.setTextContent(this.labelEl_, this.label_); + } +}; + + +/** + * @return {string} The filter label. + */ +goog.ui.FilteredMenu.prototype.getFilterLabel = function() { + return this.label_; +}; + + +/** + * Sets the filter string. + * @param {?string} str Filter string. + */ +goog.ui.FilteredMenu.prototype.setFilter = function(str) { + if (this.filterInput_) { + this.filterInput_.value = str; + this.filterItems_(str); + } +}; + + +/** + * Returns the filter string. + * @return {string} Current filter or an an empty string. + */ +goog.ui.FilteredMenu.prototype.getFilter = function() { + return this.filterInput_ && goog.isString(this.filterInput_.value) ? + this.filterInput_.value : + ''; +}; + + +/** + * Sets the index of first item that should be affected by the filter. Menu + * items with a lower index will not be affected by the filter. + * @param {number} index Index of first item that should be affected by filter. + */ +goog.ui.FilteredMenu.prototype.setFilterFromIndex = function(index) { + this.filterFromIndex_ = index; +}; + + +/** + * Returns the index of first item that is affected by the filter. + * @return {number} Index of first item that is affected by filter. + */ +goog.ui.FilteredMenu.prototype.getFilterFromIndex = function() { + return this.filterFromIndex_; +}; + + +/** + * Gets a list of items entered in the search box. + * @return {!Array} The entered items. + */ +goog.ui.FilteredMenu.prototype.getEnteredItems = function() { + return this.enteredItems_ || []; +}; + + +/** + * Sets whether multiple items can be entered comma separated. + * @param {boolean} b Whether multiple items can be entered. + */ +goog.ui.FilteredMenu.prototype.setAllowMultiple = function(b) { + this.allowMultiple_ = b; +}; + + +/** + * @return {boolean} Whether multiple items can be entered comma separated. + */ +goog.ui.FilteredMenu.prototype.getAllowMultiple = function() { + return this.allowMultiple_; +}; + + +/** + * Sets whether the specified child should be affected (shown/hidden) by the + * filter criteria. + * @param {goog.ui.Component} child Child to change. + * @param {boolean} persistent Whether the child should be persistent. + */ +goog.ui.FilteredMenu.prototype.setPersistentVisibility = function( + child, persistent) { + if (!this.persistentChildren_) { + this.persistentChildren_ = {}; + } + this.persistentChildren_[child.getId()] = persistent; +}; + + +/** + * Returns whether the specified child should be affected (shown/hidden) by the + * filter criteria. + * @param {goog.ui.Component} child Menu item to check. + * @return {boolean} Whether the menu item is persistent. + */ +goog.ui.FilteredMenu.prototype.hasPersistentVisibility = function(child) { + return !!( + this.persistentChildren_ && this.persistentChildren_[child.getId()]); +}; + + +/** + * Handles filter input events. + * @param {goog.events.BrowserEvent} e The event object. + */ +goog.ui.FilteredMenu.prototype.handleFilterEvent = function(e) { + this.filterItems_(this.filterInput_.value); + + // Highlight the first visible item unless there's already a highlighted item. + var highlighted = this.getHighlighted(); + if (!highlighted || !highlighted.isVisible()) { + this.highlightFirst(); + } + this.dispatchEvent(goog.ui.FilteredMenu.EventType.FILTER_CHANGED); +}; + + +/** + * Shows/hides elements based on the supplied filter. + * @param {?string} str Filter string. + * @private + */ +goog.ui.FilteredMenu.prototype.filterItems_ = function(str) { + // Do nothing unless the filter string has changed. + if (this.filterStr_ == str) { + return; + } + + if (this.labelEl_) { + this.labelEl_.style.visibility = str == '' ? 'visible' : 'hidden'; + } + + if (this.allowMultiple_ && this.enteredItems_) { + // Matches all non space characters after the last comma. + var lastWordRegExp = /^(.+),[ ]*([^,]*)$/; + var matches = str.match(lastWordRegExp); + // matches[1] is the string up to, but not including, the last comma and + // matches[2] the part after the last comma. If there are no non-space + // characters after the last comma matches[2] is undefined. + var items = matches && matches[1] ? matches[1].split(',') : []; + + // If the number of comma separated items has changes recreate the + // entered items array and fire a change event. + if (str.substr(str.length - 1, 1) == ',' || + items.length != this.enteredItems_.length) { + var lastItem = items[items.length - 1] || ''; + + // Auto complete text in input box based on the highlighted item. + if (this.getHighlighted() && lastItem != '') { + var caption = this.getHighlighted().getCaption(); + if (caption.toLowerCase().indexOf(lastItem.toLowerCase()) == 0) { + items[items.length - 1] = caption; + this.filterInput_.value = items.join(',') + ','; + } + } + this.enteredItems_ = items; + this.dispatchEvent(goog.ui.Component.EventType.CHANGE); + this.setHighlightedIndex(-1); + } + + if (matches) { + str = matches.length > 2 ? goog.string.trim(matches[2]) : ''; + } + } + + var matcher = + new RegExp('(^|[- ,_/.:])' + goog.string.regExpEscape(str), 'i'); + for (var child, i = this.filterFromIndex_; child = this.getChildAt(i); i++) { + if (child instanceof goog.ui.FilterObservingMenuItem) { + child.callObserver(str); + } else if (!this.hasPersistentVisibility(child)) { + // Only show items matching the filter and highlight the part of the + // caption that matches. + var caption = child.getCaption(); + if (caption) { + var matchArray = caption.match(matcher); + if (str == '' || matchArray) { + child.setVisible(true); + var pos = caption.indexOf(matchArray[0]); + + // If position is non zero increase by one to skip the separator. + if (pos) { + pos++; + } + this.boldContent(child, pos, str.length); + } else { + child.setVisible(false); + } + } else { + // Hide separators and other items without a caption if a filter string + // has been entered. + child.setVisible(str == ''); + } + } + } + this.filterStr_ = str; +}; + + +/** + * Updates the content of the given menu item, bolding the part of its caption + * from start and through the next len characters. + * @param {!goog.ui.Control} child The control to bold content on. + * @param {number} start The index at which to start bolding. + * @param {number} len How many characters to bold. + * @protected + */ +goog.ui.FilteredMenu.prototype.boldContent = function(child, start, len) { + var caption = child.getCaption(); + var boldedCaption; + if (len == 0) { + boldedCaption = this.getDomHelper().createTextNode(caption); + } else { + var preMatch = caption.substr(0, start); + var match = caption.substr(start, len); + var postMatch = caption.substr(start + len); + boldedCaption = this.getDomHelper().createDom( + goog.dom.TagName.SPAN, null, preMatch, + this.getDomHelper().createDom(goog.dom.TagName.B, null, match), + postMatch); + } + var accelerator = child.getAccelerator && child.getAccelerator(); + if (accelerator) { + child.setContent([ + boldedCaption, this.getDomHelper().createDom( + goog.dom.TagName.SPAN, + goog.ui.MenuItem.ACCELERATOR_CLASS, accelerator) + ]); + } else { + child.setContent(boldedCaption); + } +}; + + +/** + * Handles the menu's behavior for a key event. The highlighted menu item will + * be given the opportunity to handle the key behavior. + * @param {goog.events.KeyEvent} e A browser event. + * @return {boolean} Whether the event was handled. + * @override + */ +goog.ui.FilteredMenu.prototype.handleKeyEventInternal = function(e) { + // Home, end and the arrow keys are normally used to change the selected menu + // item. Return false here to prevent the menu from preventing the default + // behavior for HOME, END and any key press with a modifier. + if (e.shiftKey || e.ctrlKey || e.altKey || + e.keyCode == goog.events.KeyCodes.HOME || + e.keyCode == goog.events.KeyCodes.END) { + return false; + } + + if (e.keyCode == goog.events.KeyCodes.ESC) { + this.dispatchEvent(goog.ui.Component.EventType.BLUR); + return true; + } + + return goog.ui.FilteredMenu.superClass_.handleKeyEventInternal.call(this, e); +}; + + +/** + * Sets the highlighted index, unless the HIGHLIGHT event is intercepted and + * cancelled. -1 = no highlight. Also scrolls the menu item into view. + * @param {number} index Index of menu item to highlight. + * @override + */ +goog.ui.FilteredMenu.prototype.setHighlightedIndex = function(index) { + goog.ui.FilteredMenu.superClass_.setHighlightedIndex.call(this, index); + var contentEl = this.getContentElement(); + var el = /** @type {!HTMLElement} */ ( + this.getHighlighted() ? this.getHighlighted().getElement() : null); + if (this.filterInput_) { + goog.a11y.aria.setActiveDescendant(this.filterInput_, el); + } + + if (el && goog.dom.contains(contentEl, el)) { + var contentTop = goog.userAgent.IE && !goog.userAgent.isVersionOrHigher(8) ? + 0 : + contentEl.offsetTop; + + // IE (tested on IE8) sometime does not scroll enough by about + // 1px. So we add 1px to the scroll amount. This still looks ok in + // other browser except for the most degenerate case (menu height <= + // item height). + + // Scroll down if the highlighted item is below the bottom edge. + var diff = (el.offsetTop + el.offsetHeight - contentTop) - + (contentEl.clientHeight + contentEl.scrollTop) + 1; + contentEl.scrollTop += Math.max(diff, 0); + + // Scroll up if the highlighted item is above the top edge. + diff = contentEl.scrollTop - (el.offsetTop - contentTop) + 1; + contentEl.scrollTop -= Math.max(diff, 0); + } +}; + + +/** + * Handles clicks on the filter label. Focuses the input element. + * @param {goog.events.BrowserEvent} e A browser event. + * @private + */ +goog.ui.FilteredMenu.prototype.onFilterLabelClick_ = function(e) { + this.filterInput_.focus(); +}; + + +/** @override */ +goog.ui.FilteredMenu.prototype.getContentElement = function() { + return this.contentElement_ || this.getElement(); +}; + + +/** + * Returns the filter input element. + * @return {Element} Input element. + */ +goog.ui.FilteredMenu.prototype.getFilterInputElement = function() { + return this.filterInput_ || null; +}; + + +/** @override */ +goog.ui.FilteredMenu.prototype.decorateInternal = function(element) { + this.setElementInternal(element); + + // Decorate the menu content. + this.decorateContent(element); + + // Locate internally managed elements. + var el = this.getDomHelper().getElementsByTagNameAndClass( + goog.dom.TagName.DIV, + goog.getCssName(this.getRenderer().getCssClass(), 'filter'), element)[0]; + this.labelEl_ = goog.dom.getFirstElementChild(el); + this.filterInput_ = goog.dom.getNextElementSibling(this.labelEl_); + this.contentElement_ = goog.dom.getNextElementSibling(el); + + // Decorate additional menu items (like 'apply'). + this.getRenderer().decorateChildren( + this, + /** @type {!Element} */ (el.parentNode), this.contentElement_); + + this.initFilterInput_(); +}; diff --git a/closure-library/closure/goog/ui/filterobservingmenuitem.js b/closure-library/closure/goog/ui/filterobservingmenuitem.js new file mode 100644 index 0000000000..5ef5d91de8 --- /dev/null +++ b/closure-library/closure/goog/ui/filterobservingmenuitem.js @@ -0,0 +1,98 @@ +// Copyright 2007 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 Menu item observing the filter text in a + * {@link goog.ui.FilteredMenu}. The observer method is called when the filter + * text changes and allows the menu item to update its content and state based + * on the filter. + * + * @author eae@google.com (Emil A Eklund) + */ + +goog.provide('goog.ui.FilterObservingMenuItem'); + +goog.require('goog.ui.FilterObservingMenuItemRenderer'); +goog.require('goog.ui.MenuItem'); +goog.require('goog.ui.registry'); + + + +/** + * Class representing a filter observing menu item. + * + * @param {goog.ui.ControlContent} content Text caption or DOM structure to + * display as the content of the item (use to add icons or styling to + * menus). + * @param {*=} opt_model Data/model associated with the menu item. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper used for + * document interactions. + * @param {goog.ui.MenuItemRenderer=} opt_renderer Optional renderer. + * @constructor + * @extends {goog.ui.MenuItem} + */ +goog.ui.FilterObservingMenuItem = function( + content, opt_model, opt_domHelper, opt_renderer) { + goog.ui.MenuItem.call( + this, content, opt_model, opt_domHelper, + opt_renderer || new goog.ui.FilterObservingMenuItemRenderer()); +}; +goog.inherits(goog.ui.FilterObservingMenuItem, goog.ui.MenuItem); +goog.tagUnsealableClass(goog.ui.FilterObservingMenuItem); + + +/** + * Function called when the filter text changes. + * @type {Function} function(goog.ui.FilterObservingMenuItem, string) + * @private + */ +goog.ui.FilterObservingMenuItem.prototype.observer_ = null; + + +/** @override */ +goog.ui.FilterObservingMenuItem.prototype.enterDocument = function() { + goog.ui.FilterObservingMenuItem.superClass_.enterDocument.call(this); + this.callObserver(); +}; + + +/** + * Sets the observer functions. + * @param {Function} f function(goog.ui.FilterObservingMenuItem, string). + */ +goog.ui.FilterObservingMenuItem.prototype.setObserver = function(f) { + this.observer_ = f; + this.callObserver(); +}; + + +/** + * Calls the observer function if one has been specified. + * @param {?string=} opt_str Filter string. + */ +goog.ui.FilterObservingMenuItem.prototype.callObserver = function(opt_str) { + if (this.observer_) { + this.observer_(this, opt_str || ''); + } +}; + + +// Register a decorator factory function for +// goog.ui.FilterObservingMenuItemRenderer. +goog.ui.registry.setDecoratorByClassName( + goog.ui.FilterObservingMenuItemRenderer.CSS_CLASS, function() { + // FilterObservingMenuItem defaults to using + // FilterObservingMenuItemRenderer. + return new goog.ui.FilterObservingMenuItem(null); + }); diff --git a/closure-library/closure/goog/ui/filterobservingmenuitemrenderer.js b/closure-library/closure/goog/ui/filterobservingmenuitemrenderer.js new file mode 100644 index 0000000000..a906916ef1 --- /dev/null +++ b/closure-library/closure/goog/ui/filterobservingmenuitemrenderer.js @@ -0,0 +1,64 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Menu item observing the filter text in a + * {@link goog.ui.FilteredMenu}. The observer method is called when the filter + * text changes and allows the menu item to update its content and state based + * on the filter. + * + * @author eae@google.com (Emil A Eklund) + */ + +goog.provide('goog.ui.FilterObservingMenuItemRenderer'); + +goog.require('goog.ui.MenuItemRenderer'); + + + +/** + * Default renderer for {@link goog.ui.FilterObservingMenuItem}s. Each item has + * the following structure: + * + *
      ...(content)...
      + * + * @constructor + * @extends {goog.ui.MenuItemRenderer} + * @final + */ +goog.ui.FilterObservingMenuItemRenderer = function() { + goog.ui.MenuItemRenderer.call(this); +}; +goog.inherits( + goog.ui.FilterObservingMenuItemRenderer, goog.ui.MenuItemRenderer); +goog.addSingletonGetter(goog.ui.FilterObservingMenuItemRenderer); + + +/** + * CSS class name the renderer applies to menu item elements. + * @type {string} + */ +goog.ui.FilterObservingMenuItemRenderer.CSS_CLASS = + goog.getCssName('goog-filterobsmenuitem'); + + +/** + * Returns the CSS class to be applied to menu items rendered using this + * renderer. + * @return {string} Renderer-specific CSS class. + * @override + */ +goog.ui.FilterObservingMenuItemRenderer.prototype.getCssClass = function() { + return goog.ui.FilterObservingMenuItemRenderer.CSS_CLASS; +}; diff --git a/closure-library/closure/goog/ui/flatbuttonrenderer.js b/closure-library/closure/goog/ui/flatbuttonrenderer.js new file mode 100644 index 0000000000..ac91b7d0b7 --- /dev/null +++ b/closure-library/closure/goog/ui/flatbuttonrenderer.js @@ -0,0 +1,147 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Similar functionality of {@link goog.ui.ButtonRenderer}, + * but uses a
      element instead of a