From 06942e88405a9a0acbde0f621d4d1a3dbdd955ce Mon Sep 17 00:00:00 2001 From: Daniel Wagner-Hall Date: Mon, 21 Sep 2026 13:37:33 +0100 Subject: [PATCH] Vendor codelab CSS and JS --- .github/workflows/build.yml | 4 +- build.sh | 15 + netlify.toml | 2 +- static/LICENSE | 202 ++++ static/README.md | 180 ++++ static/codelab-elements.css | 9 + static/codelab-elements.js | 271 +++++ static/codelab-index.css | 3 + static/codelab-index.js | 56 + static/custom-elements.min.js | 38 + static/custom-elements.min.js.map | 1 + static/native-shim.js | 164 +++ static/prettify.js | 1655 +++++++++++++++++++++++++++++ 13 files changed, 2596 insertions(+), 4 deletions(-) create mode 100755 build.sh create mode 100644 static/LICENSE create mode 100644 static/README.md create mode 100755 static/codelab-elements.css create mode 100755 static/codelab-elements.js create mode 100755 static/codelab-index.css create mode 100755 static/codelab-index.js create mode 100755 static/custom-elements.min.js create mode 100755 static/custom-elements.min.js.map create mode 100755 static/native-shim.js create mode 100755 static/prettify.js diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5e58db9..5acd5d8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,7 +9,5 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Install claat - run: ./download-claat-linux.sh - name: Build - run: PATH="/tmp/claat:${PATH}" python3 build.py + run: ./build.sh diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..d5bade9 --- /dev/null +++ b/build.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +set -euo pipefail + +./download-claat-linux.sh +PATH="/tmp/claat:${PATH}" python3 ./build.py + +sed -e 's#https://storage.googleapis.com/claat-public#/static#g' -i build/*/index.html + +# The public static hosting seems offline (e.g. https://storage.googleapis.com/claat-public/codelab-elements.css) so we vendor these assets: +# Built by running `USE_BAZEL_VERSION=0.19.1 bazel build :bundle` with patches to: +# 1. `//codelab-elements/google-codelab-analytics`: `suppress = ["strictDependencies", "missingProvide", "checkVars", "missingProperties", "unusedLocalVariables", "unusedLocalVariables"],` +# 2. `//codelab-elements/google-codelab`: `suppress = ["unusedLocalVariables"],` +mkdir build/static +cp static/* build/static/ diff --git a/netlify.toml b/netlify.toml index 4a8d745..964b265 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,7 +1,7 @@ [build] ignore = "exit 1" #always build on PR even if you can't detect changes in this folder publish = "build" -command = './download-claat-linux.sh && PATH="/tmp/claat:${PATH}" python3 ./build.py' +command = './build.sh' [build.environment] PYTHON_VERSION="3.13" diff --git a/static/LICENSE b/static/LICENSE new file mode 100644 index 0000000..420f8fb --- /dev/null +++ b/static/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2016 Google Inc. 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. diff --git a/static/README.md b/static/README.md new file mode 100644 index 0000000..d8741ec --- /dev/null +++ b/static/README.md @@ -0,0 +1,180 @@ +# Codelab Custom Elements + +The next generation of the codelab elements without any framework or library +dependencies, only the [Custom Elements](https://html.spec.whatwg.org/multipage/custom-elements.html) +standard spec. + +If this is a release bundle, produced with a `bazel build :bundle` command, +you should see `codelab-elements.js`, `codelab-elements.css` and other files, +ready to be added to an HTML page like the following. Only relevant parts are shown: + +```html + + + + + + A codelab demo + + + + + + + + + Contents of the first step. + + + Contents of the second step. + + + + + + + + +``` + +You can download the latest version +from https://github.com/googlecodelabs/codelab-elements. + +## Dev environment + +All you need is [bazel](https://docs.bazel.build/versions/master/install.html). + +**Currently, the build process requires bazel version 0.18.1.** + +After bazel is installed, try executing the following: + + bazel test --test_output=all //codelab-elements/demo:hello_test + +It will take some time at the first run because bazel will download and compile +all dependencies needed to work with the code and run tests. This includes +Google Closure library and compiler, Go language and browsers to run local JS +tests on. + +### Building + +Check out a demo HelloElement target. To build the element, execute the following: + + bazel build //codelab-elements/demo:hello_bin + +It should output something like this: + + INFO: Analysed target //demo:hello_bin (0 packages loaded). + INFO: Found 1 target... + Target //demo:hello_bin up-to-date: + bazel-bin/demo/hello_bin.js + bazel-bin/demo/hello_bin.js.map + INFO: Elapsed time: 0.716s, Critical Path: 0.03s + INFO: Build completed successfully, 1 total action + +### Testing + +All elements should have their test targets. +As a starting point, check out HelloElement tests: + + bazel test --test_output=errors //codelab-elements/demo:hello_test + +You should see something like this: + + INFO: Elapsed time: 5.394s, Critical Path: 4.60s + INFO: Build completed successfully, 2 total actions + //codelab-elements/demo:hello_test_chromium-local PASSED in 4.6s + +When things go wrong, it is usually easier to inspect and analyze output +with debug enabled: + + bazel test -s --verbose_failures --test_output=all --test_arg=-debug codelab-elements/demo/hello_test + +### Manual inspection from a browser + +To browse things around manually with a real browser, execute the following: + + bazel run //codelab-elements/tools:server + +and navigate to http://localhost:8080. + +# Deploying the built artifacts to a CDN + +We now store the built artifacts in a world-readable Google Cloud Storage bucket (gs://codelab-elements). The list of artifacts we need to serve from this bucket are as follows: + +- codelab-elements.css +- native-shim.js +- custom-elements.min.js +- prettify.js +- codelab-elements.js + +The manual process for deploying these artifacts is as follows: + +``` +cd googlecodelabs/tools # start at repo level +bazel build //... # build everything +mkdir pkg +cd pkg +unzip ../bazel-genfiles/bundle.zip +export SRC="codelab-elements.css native-shim.js custom-elements.min.js prettify.js codelab-elements.js codelab-index.css codelab-index.js" +gsutil -m cp -a public-read $SRC gs://codelab-elements +``` +Then you need to include these artifacts using their full path, which is: + +`https://storage.googleapis.com/codelab-elements/FILE` + +where 'FILE' is replaced by one of the source files defined in the SRC variable above. + +Here's an index.html obtaining the codelab-elements from Google Cloud Storage: + +``` + + + + + + A codelab demo + + + + + + + + + Contents of the first step. + + + Contents of the second step. + + + + + + + + +``` + +## NPM + +The library is published on NPM.js at: https://www.npmjs.com/package/codelab-elements + +We also support a build workflow using NPM. To build the library run from the root of the repo: + +```bash +# Install dependencies. This takes care of installing the right version of Bazel. +npm install + +# Build the library +npm run build + +# The output is a zip file under bazel-genfiles +ls bazel-genfiles npm_dist.zip + +# Publish a new version of the library to NPM +npm run dist +``` + +## Notes + +This is not an official Google product. diff --git a/static/codelab-elements.css b/static/codelab-elements.css new file mode 100755 index 0000000..cd9ea35 --- /dev/null +++ b/static/codelab-elements.css @@ -0,0 +1,9 @@ +google-codelab-about .codelab-title{width:100%;text-align:center;font-size:32px;margin:20px auto 30px auto;line-height:45px;max-width:700px}google-codelab-about .codelab-title .token{display:inline-block;margin:0 5px}google-codelab-about .about-card{display:flex;flex-direction:column;border:1px #dadce0 solid;border-radius:5px;background:#fff;max-width:800px;margin:0 auto 30px auto;padding:20px}google-codelab-about .about-card h2{font-weight:500;font-weight:400 !important;line-height:1em !important;margin:0 !important;font-size:28px}google-codelab-about .about-card>*:not(:first-child){padding-top:30px}google-codelab-about .about-card .material-icons{margin-right:10px;top:6px;position:relative} + +/*# sourceMappingURL=google_codelab_about_scss_bin.css.map */google-codelab-step{line-height:24px;display:block}google-codelab-step:focus{outline:none}google-codelab-step pre,google-codelab-step code{font-family:'Source Code Pro', Helvetica, Arial;font-size:inherit;border-radius:4px;overflow-x:auto;overflow-y:visible}google-codelab-step code{background-color:#E8EAEd;padding:0.1em 0.3em}google-codelab-step pre{display:block;color:white;background-color:#28323f;padding:14px;-webkit-text-size-adjust:none;line-height:1.4}google-codelab-step pre>code{padding:0;background-color:transparent}google-codelab-step code em{color:#97C8F2}google-codelab-step pre .str,google-codelab-step code .str{color:#34A853}google-codelab-step pre .kwd,google-codelab-step code .kwd{color:#F538A0}google-codelab-step pre .com,google-codelab-step code .com{color:#BDC1C6}google-codelab-step pre .typ,google-codelab-step code .typ{color:#24C1E0}google-codelab-step pre .lit,google-codelab-step code .lit{color:#4285F4}google-codelab-step pre .pun,google-codelab-step code .pun{color:#F8F9FA}google-codelab-step pre .pln,google-codelab-step code .pln{color:#F8F9FA}google-codelab-step pre .tag,google-codelab-step code .tag{color:#24C1E0}google-codelab-step pre .atn,google-codelab-step code .atn{color:#EDA912}google-codelab-step pre .atv,google-codelab-step code .atv{color:#34A853}google-codelab-step pre .dec,google-codelab-step code .dec{color:#5195EA}google-codelab-step paper-button{display:inline-flex;align-items:center;justify-content:center;position:relative;box-sizing:border-box;min-width:5.14em;margin:0 0.29em;background:transparent;-webkit-tap-highlight-color:transparent;font:inherit;text-transform:uppercase;outline-width:0;border-radius:3px;user-select:none;cursor:pointer;z-index:0;padding:0.7em 0.57em;font-family:'Roboto', 'Noto', sans-serif;-webkit-font-smoothing:antialiased;box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 1px 5px 0 rgba(0,0,0,0.12),0 3px 1px -2px rgba(0,0,0,0.2)}google-codelab-step h2.step-title{font-family:'Google Sans', Arial, sans-serif !important;font-size:28px !important;font-weight:400 !important;line-height:1em !important;margin:0 0 30px !important}google-codelab-step .step-title a{color:#3C4043;text-decoration:none}google-codelab-step .step-title a:focus,google-codelab-step .step-title a:hover{color:#212121;text-decoration:underline}google-codelab:not([theme="minimal"]) google-codelab-step .instructions{box-shadow:0px 1px 2px 0px rgba(60,64,67,0.3),0px 2px 6px 2px rgba(60,64,67,0.15);background:#fff;max-width:800px;font-size:14px;margin:0 auto;margin-bottom:90px;border-radius:4px}google-codelab-step .instructions .inner{padding:24px}google-codelab[theme="minimal"] google-codelab-step .instructions .inner{padding:0 24px}@media (max-width: 800px){google-codelab .instructions{margin:0 0 16px 0}}google-codelab:not([theme="minimal"]) google-codelab-step .instructions :not(.step-title)>a,google-codelab:not([theme="minimal"]) google-codelab-step .instructions :not(.step-title)>a:visited{color:#1A73E8}google-codelab:not([theme="minimal"]) google-codelab-step .instructions h2,google-codelab:not([theme="minimal"]) google-codelab-step .instructions h3,google-codelab:not([theme="minimal"]) google-codelab-step .instructions h4{font-weight:400;margin:0}google-codelab:not([theme="minimal"]) google-codelab-step .instructions h2{font-weight:300;line-height:1em;font-size:22px}google-codelab:not([theme="minimal"]) google-codelab-step .instructions{line-height:24px}google-codelab:not([theme="minimal"]) google-codelab-step .instructions li{margin:0.5em 0}google-codelab:not([theme="minimal"]) google-codelab-step .instructions h2{font-weight:500;margin:20px 0 0 0;font-size:20px}google-codelab:not([theme="minimal"]) google-codelab-step .instructions h3{font-size:16px;font-weight:500;margin:20px 0 0 0}google-codelab:not([theme="minimal"]) google-codelab-step .instructions aside{padding:0.5em 1em;margin:2em 0;border-left:4px solid;border-radius:4px}google-codelab:not([theme="minimal"]) google-codelab-step .instructions aside p{margin:0.5em 0}google-codelab:not([theme="minimal"]) google-codelab-step .instructions aside.note,google-codelab:not([theme="minimal"]) google-codelab-step .instructions aside.notice{border-color:#EA8600;background:#FEF7E0;color:#212124}google-codelab:not([theme="minimal"]) google-codelab-step .instructions aside.tip,google-codelab:not([theme="minimal"]) google-codelab-step .instructions aside.special{border-color:#137333;background:#E6F4EA;color:#212124}google-codelab:not([theme="minimal"]) google-codelab-step .instructions aside.warning{border-color:#EA8600;background:#FEF7E0;color:#212124}google-codelab-step .instructions aside.callout{background-color:#E8F0FE;margin:20px 0;padding:15px;border-left:3px solid #185ABC;border-radius:4px;color:#212124;font-size:14px;line-height:1.5}google-codelab-step aside.callout b{color:#185ABC}google-codelab-step .instructions ul.checklist{list-style:none;padding:0 0 0 1em}google-codelab-step .instructions ul.checklist li{padding-left:24px;background-size:20px;background-repeat:no-repeat;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAAWlBMVEUAAAAxokwwoks1pFAxokwxokwxokwxokwxokwnnkQnnkQnnkRou3y84cTS69cxokwonkQxokwnnkRqvH1VsmtluXlVsmsnnkRdtnLw+PIxokwqn0YinEAfmj3goh/UAAAAGnRSTlMA2CcEo+6AQT7+2IOBJxPl27alhoBnX15SCCe258UAAAB+SURBVEjH7dA5EoAgEERR3BcQ923Q+1/T0SqKlNbMouP3gxkRFvZpyQb64VSQT4mOcYc8mU5DnqIG8zXoozj4d34tML+YrET8XBFx4e2F4oAL4N7J3EUB/EfSUwD/zG3hvFdROu9XtL31vgXguQA9F6DnAvM8WbOHpkXYD3cBBCcPjtASYjwAAAAASUVORK5CYII=")}google-codelab-step .instructions table code,google-codelab-step .instructions h2 code{background:white}google-codelab-step .instructions .indented{margin-left:40px}google-codelab-step .instructions strong{font-weight:600}google-codelab-step .instructions :link paper-button{text-decoration:none !important}google-codelab-step .instructions paper-button{display:inline-block;border-radius:4px;color:#FFFFFF;font-family:'Google Sans', Arial, sans-serif;font-size:14px;font-weight:600;letter-spacing:.6px;padding-bottom:6px;padding-left:12px;padding-right:16px;padding-top:6px;text-transform:none}google-codelab-step .instructions paper-button a{text-decoration:none;color:inherit !important}google-codelab-step a paper-button{display:inline-block}google-codelab-step .instructions paper-button.colored{background-color:#1E8E3E}google-codelab-step .instructions paper-button.red{background-color:#D93025}google-codelab-step .instructions iron-icon{vertical-align:sub;margin-right:7px;margin-left:3px;font-size:16px;top:-1px;position:relative}iron-icon{font:normal normal normal 24px/1 "Material Icons";-webkit-font-feature-settings:"liga";font-feature-settings:"liga";-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;text-transform:none;word-wrap:normal;font-size:inherit}iron-icon[icon=file-download]::after{content:"cloud_download"}google-codelab-step .instructions img{max-width:100%;vertical-align:bottom}google-codelab-step .instructions .image-container{text-align:center}google-codelab-step .instructions table{border-spacing:0}google-codelab-step .instructions td{vertical-align:top;border-bottom:1px solid #CCC;padding:8px}google-codelab-step .instructions table p{margin:0}google-codelab:not([theme="minimal"]) .instructions h3.faq{border-bottom:1px solid #ddd}google-codelab:not([theme="minimal"]) .instructions ul.faq{list-style:none;padding-left:1em}google-codelab:not([theme="minimal"]) .instructions .faq li{font-size:1.1em;margin-bottom:0.8em}google-codelab:not([theme="minimal"]) .instructions .faq a{color:inherit;text-decoration:none}google-codelab:not([theme="minimal"]) .instructions .faq a:hover{text-decoration:underline}google-codelab-step .instructions .faq a[href*="cloud.google.com"]{padding-left:22px;background-size:20px;background-repeat:no-repeat;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAC9FBMVEX////u7u7v7+/ZRDf/zEH/zkPbRTlJifT/z0XYRDhRj/XWRDdPjvVOjfX/zkH/z0fXQzdGiPXUQzdMi/VKi/Xr6+3t7e1SkPXbRDdLjPW70fPaQjVOjPXx8PD9/f1nnff19fXRQzdHifVJhOxEh/Tu7/JLifH9y0DUQjVtofhOi/NKh+9HgelGf+b+7saaWXX/zDz+yTrZRzrRQjT7+/vxx8PeqkTsuj32uzrOQzdqn/j39/fr6Oj9yT/3vjvaQDPUPTBJivVKifPq7O/z0Mz968T4wj7zwD3NPTHGPDD0+P9Bifrv8/Xx8vJJhu5EfOPo3Nv/0Ez8y0rxvT3ZPjBDi/tBhPT55ePkysj+3Hr/0VLHVErcpkPcTUH7xD35wTzztznpsTjKRTjo8P3U4/z6+vr//PmQt/mBrff//fayy/ZUkfZjmfSXufP78/L18+//+Oj66Of+9eBCdt3t59f/89TvxcPsurXasKztqKL+4Y7jjIT0033ShH3hc2ngaV7/1F3/0ljjv1fbWU37xkDaoEDCSj/xujvptjvutjrxtjn8xDflrDb5vjXJQjX2tzP4+v+bwf3I3Pyiw/t0pvhel/VZlPW90fP/+/DV3/Cdu+/e4uv33Nk+btP+8Mz+8Mniwb7+7L3u371Vdrv+6bDusq3+56j+5aTx2Z+Gjpj+4pbolI3RjYehnIT+2XPedW370mbHaGDMZl3bY1jQVUvbU0f/00PYTkLKSj/tsjjwpjfeXjfnfjbEQjbXPjTSPS/w9vu50vs7hfr5+flypPf89vb89fTF1fHM2vBsnfB3ou/67+6swOpekert6uNdi+M5deLq4OA/dt83bt80a9xmjdby19VKc8jx5cf12cP/7r/gwr+pnr/v4b7xwLtffbf+6rHx3KnorKjYqKTXpqHXo57aop3ZkovTwIboh3+jX3f30XDgenCWUm3Kc2zIsGnMcGj4y1zgYFXgX1TfrEXWlz7mhjfsrTbkljbabDbcVTbsmTBDSJ02AAAFFklEQVRYw+2WV1jTUABGkyZpi6W0tKUWC0hBsAporbMWrCgqKA6WCooK7r333nvvvffee++99957b33x3uTetIEGnnzx83y89Tunf25SWuI/f4Oqk6cU4ijLkr9sfp5Ckyfk6h8o5JkH4umpVPr5+fnqdLro6GiZTBYkk3l71z5SNbdA8zyt8wLYhKefUunr66vV6WQsMDExF3/StLytka/0VPopfbUgoEWBoCBv76lNcj6AFnnQ+6MB0BcsKNA8x2M4iPbjAThQgVsAA545TWg8RQlc5MMBflrg8wHgg3NsIX6OAa10Lr6SDQgHgAXetSeJD5iqRTrytfAKdHwgiAu0EZ3QvEBhYLLAu6+LDoIOtCApKSnVUqoBDondyiahoTggazt9ej4hd94Wxbya4z5wskZoYVgA0/NXlGSlVr3iJpOpOMsbt36r4BphsACQHSezBfSP4zQQs8Zs3n7P3QmeDQ8OAwVwdkEt0ADhhI3jFCxms7lfu+yBthHhwcFhoaCgnXZC4obqPYbZPABqgHVpNj/yak1QABdRQatriwcIiXy312alrBRFqff175jFb9lrZecIVGgz09+NTurjbw/LNFmtUqnVw8PRM0AY6JowaHFN7iIKz2rvtCIjfbBPkvF992aapABKakzvKByQymxbO5e7iDb8BfjoA5uWsPv7kJxPSi4Oy7RpgA/+kl8KArdolUq1uDMshLXCA8jD6NXqpEQPAmDCszibTSFlccS4+DMGMCrVtvdzO0eEh59CPulfgsDYoQ/Rb4q1mbiCsX8zZ2AVrQLIV16uGRGMTxD6PIEkh2TBOPA4og09nSeYJoe+fND1SxdmSRCBgg+6PypE9o01mTRq6Bs+1yEQqcCHJK69cn62xDnA3YS6W4qD51kBA9LV6MX5aXKuIB90/wyJnztCQFMJKuif2zQANfANn9CnchlYzyWYF7OFV8ATgAPV624BAzQKCgTSSxIsDxIYOQR00m7kFgjpsQP6CgUlNWxFd7LDAEaOoPvoc74ESa1NsUBXK9QelLHfafTqclZmKwXvsjpJihyi/mkc9j3Su/P7BngxeMJHPXoru9vbCP4vYZ9aT/DMpxkAW7CsCCFZ3D5I5BowAPnp1wgnqRaGQ84kduEKemchwC5BJ1jX6Tt6Ey50SvBiMH1AgCvYA1i9BP8UkRtjsW8c3E5wjcstfCBxUQgW/APt9sBAfxIR/2ScBvnS5O6EgA4baL7w+hzpRELyhNSqZ1Jgf/0xQsgSuXPCQzxBSPwaG/YpawyRhRnrvBh0DvSGLkITn2A9M/Kljt5HiawsTGAwxVbohS7+JGPfOBgNEJDK3wg60d2EBTvM2KdWE27opPLCWPqEZP9W+BCLfIoa3JFwxyoLX4haVNFHSMUecQrsGx8RbmmZxgfoIV9KIeoDhg4d+jXTzPubCREWWmhOB0SV5hgxYkRGxpgxY36i+2cEgRixQLN1FpqnYJWkKklJScNLjxy5MyPjhwf0Kegn9yZEmZdAuxQqVwEkDR8+auTO77uhD3XKsbWOeKBZL4ugULlyuXJlyjQaNeqbFfhG6BuSwdeqOF0H0q6FBmyhUaNdu6FPQd/RHw4Qpxfj4heMatCwIZywh1JTLAYD+mkhSoeBLoGoqCENYGDXL943oB834iyhLdjHgfF79mHfsb0bkRvLBhZDjB49euzYsePH/96/vzyHeXN3Inc6zSuCqcRys1tJjm4xdYj//JP8AXE5S/JuAn7MAAAAAElFTkSuQmCC")}google-codelab-step .instructions .faq a[href*="stackoverflow.com"]{padding-left:22px;background-size:24px;background-repeat:no-repeat;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAATwAAAE8CAMAAABq2/00AAAA4VBMVEUAAACCg4aCg4aCg4bIj0P1fx/2hh+ojHCojHCojHD2ih/UjCn0eiD0eiD2hB/2hR/RjjDUjCnKkkPUjCnUjCn2ih/UjCnCllPCllP2ih/CllPCllPCllP2ih/CllPCllPCllP2ih/2ih/CllPCllP2ih/2ih/CllPUjCnCllPUjCn0eiD0eiD2ih+ojHD0eiDUjCn0eiD0eiCojHDUjCn0eiDUjCn0eiDUjCnUjCn0eiCojHD0eiCojHD2ih+ojHD2ih+ojHCojHCdiXaViHuCg4aojHD0eiDCllPUjCn2ih8XYwy7AAAARXRSTlMAv0CAEEAQv4BA7++/gCBQQN8wIIDPv2Dfv5+PUDDPryCAcO+/r49wn4Bg79/frJ9QcGDPr6+Pj3DPz2AwIJ9wYOaPVDAbIL/gAAAGhElEQVR42uzbsYrCQBDG8WmyxaJJlcIixQmxMSIRgiBB7Bbm/R/oPAnH3e0SxwSvyf/3DLt8zDeMAAAAAAAAAAAAAAAAAAAAAAAAAAAA4F9k7Xm1Xwlek53WqyY87AVWfbHumvCTE5hkIZYJTFyIrQU2+xA5CmxWIULcWq1DTGBzCrFcMDluC4ENcTtDEyKdwKYLkYuAuH27ggFtuj7ETgLiNo0B7Z3c1YnNMUQaWbTC6/W1uKUPHbha7wr60GnP7ot3YpGHWCsL5Wod1JP70LMsU+H12424nfDsBj4XgzMD2kN20F82YtDSh965nf61JW7Nzy7iKwY067OLlfJcw/qx1KQPQ9zSh1aaVtGHGnxoUummrB97WZhSk3byRM/6UST3mpQRtwZbTTo4GXdhQLvbaNJVxnX0oSMftyBuDQpN8o4BzaDWpFrG5KwfH5zXpJuMCfShox83pw812GnSxrx+XPI5hjvowF7ttawfB5km+Yq4nf5xS84x5lR7nGPMqfaI2znVHgPajGqPc4w51R7nGJ/sm29P2zAQh49h1kJgJHGWlrbQItAIebNO67owadpgwOzv/4Wm8S8NLsXxGSTu/HyEk/O78+McRu0FH4pQe2EdA6P2wjoGRu2FdotQe2EdA6P2wjoGRu2FdotQe+GChlF7DNcxKulN7bFbx4h1lPlSe+x8aKK1Tqae1B6zdYxM35BLP2qP1TqGjPQdmfCi9ji125l+ICp9qD1G6xixXiSPPag9PusYiW5SCLza49JuK/2YKEOrPSYXNBFpkyTGqj0e6xiFXkoukWqPwzpGrJ8iEyi1x8GHnuknSUqU2qO/jlHpVeQpwhB8oL6OISK9mkK4qz3q7bbQzxFV7mqP9jpGqi1IYme1R3sdI060BTPpaAiot9ss0hZkwk3tUb+gyUJbkEzd1B55HxqfaQvy1Ent0V/HKCNtwVy0NwSfGPhQMdcWRJWD2uOwjiFzbcFZ3F7tsVjHmDqMLTZqj3S7bTm2RJloZwh2maxjyJnd2NJS7XFZx7AcW2QrtUe93dZUDmOLjdrbaTTa7e9fNgj1i7ZjS9lK7dU+dPvzzgaRZ8elpPZji73a2/j749vOLuWytRtbCmmv9jghLMcWuw936zfwwnZssTAEexw+VidTmstn1N4Wr2/2gcrBlO6HY3eHKNqPLYfh2FmPLeZ/aXvh2D1Q2o0tYkHt8ZxQliIybTm2NNXex30IWI8tcePDPYSAywPv+61w7NwfeHfJHrvJS44tSQmUGan+oCfAgTi3fOCly1D952A4eqkH3gTocqDuOT5NX2JsmQJZhFrkaHwy8fzAmwNdeuoRRgQiTakEugyUSR2B+LFlDoTpK4M6AvH/pUUC6JIqAyMCMQ+8pMe8E2XgGIFlxKxbAIyVgUUEWj/wUh6QAZQBIgJlzqlbQKwMUBE4Tdh0C4ChMsBFoMgiJt2ivpvVoCNQznh0CxDKAB2B9dgSA2l6ygAbgfUDbwG0GSgDPxEo5tS7xYq7GT4CU8Im6pbRsNkxMBFIex5ezqQ3qM8fPgL5kZ6Mj1QNLgKpR90y4tNjpbxFID+ExwjsAUN8ReAYmOIjAlmePF8RyLHv+orAPgScI3AIb531tQZ/XjECR3DL5VoTeDOsXTVYf8UIFHDLu6sm8GZAFA8ZgccQimcgLCPwNBRvOROLCExD8Z4mXR2BRxCK5+wCx6F47hHYC8Vzj8BJKJ5zBPYhFM85Agf0i3dx3ulcdLubm5e+I3BEv3id65r7Ov70EoGCV/EW+NrpdLu/cHWkX7zz65pn6hiK9689O8hJKIaiMCxK0nYzTBgSYxDs/pekiTohEq/v0eRKv28J/+zkXMbrF2IdxQvEC3ScOF7r6+3LrtZDa1vxVinfHaeId+qjTBCv9kGO4i1XJoi365/EWxCv9EF24i1XJ4jXv4iXKd7p/uNt+yjt/uO1PspZvOVmmGcPrZ1q3ZUi3qoDaNva4WYdj5PFu2nHMm+8nzqKF4y3vuOreL923PcrqngRz6291FrKXrxbdWzifXB6iydehHh5iDco3maQpxnivQ2yEU888cQLEi8R8cSLES8R8cSLES8R8cSLES8R8cSLES8R8QbFexzkPEO86xxA4on3F+LlIZ54QeLlIZ54QeLlIZ54QeLlId6qeOk8/BviiRciXibiiRciXibiiRciXibiiRciXibiiQcAAAAAAAAAAAAAAAAAwHTeAe21evvWi2VXAAAAAElFTkSuQmCC")}google-codelab-step .instructions .faq a[href*="support.google.com/webmasters/"]{padding-left:24px;background-size:24px;background-repeat:no-repeat;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAolBMVEUAAADW1tbW1tZ6enrQ0dJ6enrMzMzq6+zq6+x6enru7+/m5+ju7+/m5+j////S09REi/XW1tbm5+ju7+9PT0/Q0dK8vLxGjfVZWVlNTU16enrMzMzIyMj09PS/0uuSkpJpaWmbm5tim/e0tLSEhIStx+3e3+GsrKxwcHBZk/dPk/R1p/KPtu/U3eqmpqZlZWVfmO5woeqHreWvwN6nu9qLi4vZSE73AAAADnRSTlMAEcxmzO7MzDMz7u6IiHn/rpYAAALMSURBVHja7dXpjtowFIZhUkqZlSV1ncQdEsK+zD7T+7+1xjTiwxVOAmeORyP5+2UspPchEtDy8/Pz8/Pz8/M7cZeduHKdyxbL0FfVAMUraHdUHUB12i22tZWqBRSDgKFfC4CApV8PgIClXw/gE6hyTd/lAR7gAV8fEHwbWtc/ul/WfQ8IfQKAIECfCoCA3sdq+nQB+nQABIQ+CQABtY9Z+lQB+gwACBj6AFAE6DM9AL0fAalPB/yGgNAfntwHAAJKnwCAwNLneQAAVAvM/ttzVLlB4z2/A6AFVoDRf43IfewVgGKNAG/Uvrl39BsCXqh9cy8nA/51tmk63+jDZp6m9wTA4DzANiyW6lOqTxAMHAD22XASReuwpKDvELCNookBGJwx9E8B3OvsQp8e9GmOviNANA/Dh4k+TApBukHfFSBa7/I7whqf3xWA/gOAoU8DDJwBCHk+wIAw9CsAPw/W++CFB/MAD/hCgL59PXPS8toRYKHEndkTauEQIIUQ0hToG2eAXOicKdBXmSPAo9CTpkDo5U4Ad6IEGILy0gFgFJctCACIh/yATABwIChvVyE3IBR7AAQAiOmIGZADAMEBQMkRL+AJAAgOALGUI07AQgBgCPb3uRbwAaYAGAIAZlIL2AA5AIYAgJXUAjaAAsAQADCWO4EjAAQGQAvYAUsDsDQAeuwAMZvu89PZ/jZhBmQCi2dl60+MPiMAf8UgZEUpK/LoJ4oX0C9LIIyF0U8yXkBPVCzRWzIDZjX9sWQG9Kv7ScYN6KnKfizZAf2qfpLzA3p5RV9JboDeytp/kk4Ao5WtP+UFQKCO9sdTSQQ03+OR/kxKdgAWZv/11VI6AWBmP5HSNQD5zwQknwxIqIBu468hBgDqNYCuFXBDASSNAbdWQHB1PiBpDLgOWtZdXPEDri/QswjYAOjbF9x0OQHd26Dl5+fn5+fn5+dn7i/3LEaKJNV/0wAAAABJRU5ErkJggg==")}google-codelab-step .instructions .faq a[href*="developer.android.com"],google-codelab-step .instructions .faq a[href*="android-developer"]{padding-left:20px;background-repeat:no-repeat;background-size:20px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAMAAADVRocKAAAAq1BMVEX///+lyjmfxyGhyCqkyjakyTP+/vuiyS7z+OWlyjKgxyTX57ChyCfa6bTx9uLy9+Sz0l6szk/D24Tt9NrN4ZnK4JSiyCyexh34+/D6/PTp8dPh7cjf7L7k78vc6rnW5qzU5ajS5KPP4p+21GWnzD3F3Yi813Kx0ViqzUWexhr8/vjv9d7Z6LKy0lyZxADh7cLA2n2rzkr1+erJ35C61m6uz1KcxRHn8M6/2XgEePWtAAACr0lEQVRo3u3Z2XKiQBiG4Y9ebNO2aCCyzOC+7zH7/V/ZRJZQZaJpWnKS4TlJUItXbX4pFJXK71DTusvccI6z5oMSAutdhDP2uzpK4CxwxsRBGYJdA19qLAOU4s4LkyUN7G4Ude2glmzuRihHKDqAPZhNuJRKSckms37rvctrKMnG728l5cxKMU7JuCF7KEvf87h1gnu0iXK4B2p9Sc3dUgZNMusMLvu42r20LiBXT8JMWRepN1xlGu+fK8/6xFPxwsvH697/eP/j7uDTOvB+dxwX/AaM2SQ+6icrYHDyGmgfWE3irNeCqTaPAwcArydroboAbuMAb8NQRNJn20SYtHK8HaKZzoffhZl5tlNxsD5PsnUQ2b9jwwnO35WvRo3lN/o2TDwKS5M3gokF0w2wZxgIlpY2FpgsgdQPkLXJQar0A/LVIDB6Idp2GxQQrJI/rbq2Voijlc5S9LZs8uYCqBVyXLTZhLW/fSn3hDMmZAQ05I020gT2PmeM+w4uGpLksFs+oEEtbaqJVnpUkz4uyebf6xQK0CY6XjoSC1xQ9/PPr4KB52zuvTrOa5EssC0auM0C0tYKtI0DqgpUgSpQBapAFfifAj90Tn5YZoGnooH8eutB56KDDosGhlTrYiTyk0epVdFAKJLn9rLHRSNfcE4tF0UDcAXlXPhDfGPtjGejEMUDCEdPY8eFHoOADvPAxiDQLBBQkUHAJfoB34UBS/9CfAETe6lZYKZft/QIFe94vicRy7tcvKOkB0PhYOo4zlNWYNvjpjOdZwU+O24OQlynK62EGCF2J9IbpIsy/P0I3CHW+Qj8qQJVoAr8VOCx3EB+YvDSwIDmXzaW4oZZseU6Lfr5aaAUPT8uyPuT33VeIpRkI5RHyRQfHEKFUj2UJuh1GjaQsxudKECl8jv8A6GtQkKSkMLrAAAAAElFTkSuQmCC")}google-codelab-step .instructions h3>a[href*="github"],google-codelab-step .instructions h3>a[href*="github"]:visited{color:black;text-decoration:none;padding-left:24px;background-repeat:no-repeat;background-size:18px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAAB4CAMAAAAOusbgAAAAflBMVEUAAACXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZaXlZbf2s+YAAAAKXRSTlMA+SAC7QrIBPXROWMX17rCSmusXEaAD9zNeXFWJqOLMx3mtJJAK5xP4Jw4LyAAAAT6SURBVGje1NbdcqJAEAXg0zJAQEFA0QhojJHE8/4vuBfrJlvQGHSGbO13CzVT09M/g//HIssPRZAYIUkxSVAc8myBSfnrMjVUmbRc+5jEsUqFN0lazeDYogo4SlC5jPq6EI4mxRpOhKuId4pWof228y0fsJ1bbp0lfFCS4XFNSgtpg8eEO6EV2YV4QBvRWtTiXl4udEByD3fxCzpS+LhDk9CZpMFosaFDJsZItdApqTHKnM7NMULOCeQW5534zDUnUuOmWDgRiXFDYzgZ02CQn3BCiY8BXsFJFd7YQjLPkeGDTPRsRhZVK+zIABxX+yfe6Wm/OgL4YIe0UIQRO8x1jm/yiHeI8s11RdP7FKJvx649PmUBRwoyfNqza6dUktzsNt7HliNsa+9m95UGXSl7XvQn2OV0qOpzO1tsNotZe66rw+lyXbb08bcX9qToyNgjYTcoS3PaxT4UflydzLJ7nFDYk3V+SdhzQZfnQTP89VW5jPDbmRTAWvDdnAq1zFnC2pLfHHnFn9uYK3yJ+HOhZoRPa2peYe2VmjX+KKjawNKGqgJXC6HqDEtnqmSB3yrqSlgqqau+UkAVwVLEKz1tZxxQw1LNATObSNvHOqUqCGEtDKhKAcAXqlo40Ag14g92D77BiTcO9pByqNbsDfeIcvCK3+HI++AlG2piOBJTY4AjNVs4s6XmiMwitSzSK0Nu0bQs2leOAzUzODOj5qDPYvHgTCj6TA703HLoog+oxGIgWgzHBMbigWnx1DSQyTcOqBDw34Sav9q1sx1HYSAKoNcYE4cl0AlhIB22rF3//4PTI43ULbrMYjzKy5zHROiGyNhlyqAXDC5z8Acc6mcFu3+OpeCD+Y87ONMRR1isik6qet+wah3gzMEwfIPXLIsB9sQp4UxJnL2pDFRwRBHrauoEZHDkaeoV5PSKKpNydMTSCk4oTawO8O07Nva9JN+8Z0skHNgk5l3bkf7hLUcjW+CceH2B1YqeeDmzTXU5sB/EE4rZmDucsA9ksBtvJuoQq4SaDE5sqc8n2+eaNyqBOTmHtcycG8xo3B4lrMgjmZ0GrwtYaQsLbUBmImZeojKuHRbyHjRmz5ZFQRUdByfGxCPEfPJW07gbVyVoiU9xIwYz97GVs1LD5oMmbPknvcKntLmXNNDvT2GBEV1WXXyadjA0Q64bIMygLsTo00aCsbmWfCbfDOEnt6QF8EzjlBi6BasVdscU5HYwbdz9LO65y9ae4tjKwTzDTJUZfxlPJjRLhoELfdPHgNrl9ZIFK6I5LhjyxOB75QdFsqAEVIKmCW+qcZ0B9xvOPft7eReaVnFPRPmj1lPvbcJdx6toUrkB46zpmyeAnFLViMGDZvSkKfo8Y3ikAORjB8RVqonIT4+hxJjW/qRRzezNvd1VoegKoG1uGOPRhBomaktfAok/PJ9a3PykQEk1xsQ0bqtg5Pk/+04qBtryotDlG4wpaJTvzS7Q3sCwDdbhgu1sw9yi7V/9XFSEb3MwbAbXYWn5H7zF3+5J2gZHNhuPX5frsaqaOvWpsAw+rC3FY6tg/cRMYU88zya4DzGbV7oL3npYQNWugmuFZSLtIlhHWOwcrA8OzrAgT3pdsD5J2PH2a4L3HuzdStvgMscq8j2hL/e5wcm7BLA2OuBbFebVKWBirYR/D6OWc16JizqEO0WUCrHzMMrbCZFGBf5j/QYa/td1VlNoIAAAAABJRU5ErkJggg==")}google-codelab-step .youtube-video{display:flex;margin:auto;width:560px;height:315px;border:none;max-width:100%;max-height:51vw}google-codelab-step .embedded-iframe{display:flex;margin:auto;width:560px;height:315px;border:none;max-width:100%;max-height:51vw} + +/*# sourceMappingURL=google_codelab_step_scss_bin.css.map */google-codelab-survey{visibility:hidden}google-codelab-survey[upgraded]{visibility:visible}google-codelab-survey{display:block;margin:2em 0;padding:0;background:#e8f0fe;border-left:4px solid #185abc;border-radius:4px;color:#3c4043}google-codelab-survey h4{font-size:16px;font-weight:400;padding:.8em 0 0;margin:0}google-codelab-survey .survey-question-wrapper{padding:0.4em 0 1.1em 30px}google-codelab-survey .survey-question-options{padding:.8em 0 0}.survey-option-wrapper{cursor:pointer;display:block;padding:0 0 4px;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:middle}google-codelab-survey .option-text{color:#212121;font-size:16px;padding-left:24px}.survey-option-wrapper input{position:absolute;opacity:0}.custom-radio-button{position:absolute;top:5px;left:0;height:13px;width:13px;background-color:#fff;border:2px solid #3f51b5;border-radius:50%}.custom-radio-button:after{content:"";position:absolute;display:none}.survey-option-wrapper input:checked ~ .custom-radio-button:after{display:block}.survey-option-wrapper .custom-radio-button:after{top:1px;left:1px;width:7px;height:7px;border-radius:50%;background:#3f51b5} + +/*# sourceMappingURL=google_codelab_survey_scss_bin.css.map */html,body{height:100%;width:100%;margin:0;padding:0}body{font-family:"Roboto",sans-serif;transition:opacity ease-in 0.2s}*{box-sizing:border-box}[hidden]{display:none !important}google-codelab{width:100%;height:100%;display:grid;grid-template:64px 1fr 56px/256px 1fr;grid-template-areas:'title title' 'drawer main' 'metadata main'}google-codelab #main{display:flex;flex-direction:column;grid-area:main;position:relative;background:#F8F9FA}google-codelab #codelab-title{background:#FFFFFF;box-shadow:0px 1px 2px 0px rgba(60,64,67,0.3),0px 2px 6px 2px rgba(60,64,67,0.15);color:#3C4043;display:flex;grid-area:title;align-items:center;justify-content:space-between;height:64px;padding:0 36px 0 16px;-webkit-font-smoothing:antialiased;z-index:1000;max-width:100vw}google-codelab #codelab-title h1{font-size:20px;font-weight:400;margin:0 8px;font-family:'Roboto', 'Noto', sans-serif;flex-grow:1;flex-shrink:1;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;width:0;display:inline-block}google-codelab #codelab-title h1 a{color:#3C4043;text-decoration:none}google-codelab #codelab-title h1 a:focus,google-codelab #codelab-title h1 a:hover{color:#212121;text-decoration:underline}google-codelab #codelab-title .time-remaining{flex-shrink:0;flex-grow:0;display:flex;align-items:center;font-size:16px;font-weight:400;white-space:nowrap}google-codelab #codelab-title .time-remaining i{margin-right:3px}google-codelab #codelab-nav-buttons{display:flex;align-items:center;flex-grow:0;flex-shrink:0}google-codelab #codelab-nav-buttons #arrow-back,google-codelab #codelab-nav-buttons #menu{display:flex;text-decoration:none;color:#3C4043;width:40px;height:40px;justify-content:center;align-items:center}google-codelab #controls{position:absolute;bottom:32px;left:0;right:0;padding:0 32px;z-index:1001}google-codelab #fabs{display:flex;justify-content:space-between;margin:0 auto;max-width:1025px}google-codelab #previous-step,google-codelab #next-step,google-codelab #done{border-radius:4px;font-family:'Google Sans', Arial, sans-serif;font-size:14px;font-weight:600;letter-spacing:.6px;line-height:24px;padding-bottom:6px;padding-left:24px;padding-right:24px;padding-top:6px;pointer-events:initial;text-transform:none;background:#FFFFFF;color:#1A73E8;transform:scale(1, 1);transition:transform 300ms ease-in-out;box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 1px 5px 0 rgba(0,0,0,0.12),0 3px 1px -2px rgba(0,0,0,0.2);text-decoration:none;-webkit-font-smoothing:antialiased}google-codelab #next-step{color:#fff;background:#1A73E8}google-codelab #done{background:#1E8E3E;color:#fff}google-codelab #fabs a[disappear]{transform:scale(0, 0)}google-codelab #done{background:#0f9d58}google-codelab #drawer .codelab-time-container{display:none}google-codelab .metadata{background:#F8F9FA;color:#777;font-size:14px;grid-area:metadata;padding:16px}google-codelab .metadata a{color:currentcolor;margin-left:4px}google-codelab .metadata a:hover,google-codelab .metadata a:focus{color:#212121}@media (max-width: 768px){google-codelab{grid-template:64px 1fr 56px/1fr;grid-template-areas:'title' 'main' 'metadata';padding-top:0}google-codelab #codelab-title{padding:0 16px}google-codelab #codelab-title .codelab-time-container{display:none}}google-codelab #drawer{width:256px;grid-area:drawer;position:relative;z-index:100;display:flex;flex-direction:column;background:#F8F9FA;overflow:auto}google-codelab #drawer .steps{flex-shrink:1;flex-grow:1;overflow-x:visible;display:flex}google-codelab #drawer .steps:only-child{max-height:100%}google-codelab #drawer ol{margin:0;padding:16px 12px;counter-reset:li-count;list-style:none;overflow-x:visible;overflow-y:auto;flex-grow:1}google-codelab #drawer ol li{display:block;counter-increment:li-count}google-codelab #drawer ol li a{text-decoration:none;display:flex;align-items:center;font-size:14px;color:#80868B;padding:3px 10px;min-height:48px;font-weight:400;line-height:20px;box-sizing:content-box;position:relative;font-family:'Roboto', 'Noto', sans-serif;-webkit-font-smoothing:antialiased;transition:all 300ms ease-in-out;border:1px #dadce0 solid;border-radius:5px;margin:6px 0;background:white}google-codelab #drawer ol li a:active,google-codelab #drawer ol li a:focus{background:#c6c6c6;-webkit-tap-highlight-color:transparent;outline:0;border-color:#c6c6c6 !important}google-codelab #drawer ol li a .step{display:flex;align-items:center}google-codelab #drawer ol li .step:before{content:counter(li-count);display:inline-block;font-style:normal;width:26px;min-width:26px;color:#fff;background:#80868B;border-radius:50%;text-align:center;height:26px;vertical-align:middle;line-height:26px;margin-right:8px;font-weight:400;position:relative;z-index:2;transition:all 300ms ease-in-out}google-codelab #drawer ol li[selected] a,google-codelab #drawer ol li a:focus{color:#212121;font-weight:600;box-shadow:0 1px 2px 0 rgba(60,64,67,0.3),0 2px 6px 2px rgba(60,64,67,0.15)}google-codelab #drawer ol li[selected] a{border-color:#fff}google-codelab #drawer ol li[selected] .step:before{font-weight:600}google-codelab #drawer ol li[completed] a{color:#212121}google-codelab #drawer ol li[completed] .step:before{background-color:#1A73E8;color:#fff}google-codelab #codelab-nav-buttons #menu{display:none}google-codelab #drawer ol ::-webkit-scrollbar{-webkit-appearance:none;width:7px}google-codelab #drawer ol ::-webkit-scrollbar-thumb{border-radius:4px;background-color:rgba(0,0,0,0.5);-webkit-box-shadow:0 0 1px rgba(255,255,255,0.5)}@media (max-width: 768px){google-codelab #codelab-nav-buttons #arrow-back{display:none}google-codelab #codelab-nav-buttons #menu{display:flex}google-codelab #drawer{grid-area:auto;width:256px;position:absolute;left:0;top:0;bottom:0;z-index:10000;will-change:transform;box-shadow:2px 2px 4px transparent;pointer-events:none;transform:translate3d(-100%, 0, 0);transition:transform ease-in-out 0.3s,box-shadow 0.3s}google-codelab[drawer--open] #drawer{box-shadow:2px 2px 4px rgba(0,0,0,0.15);transform:translate3d(0, 0, 0);pointer-events:all}google-codelab #main::before{content:'';top:0;left:0;right:0;bottom:0;position:absolute;transition:opacity ease-in-out 0.38s;background-color:rgba(0,0,0,0.3);z-index:10;pointer-events:none;opacity:0}google-codelab[drawer--open] #main::before{opacity:1;pointer-events:all}google-codelab #drawer .codelab-time-container{display:block;padding:20px 10px 10px 23px}google-codelab #drawer .time-remaining i{margin-right:9px}}google-codelab #steps{overflow:hidden;flex-direction:column;position:relative;flex-grow:1}google-codelab google-codelab-step{display:none;width:100%;transform:translate3d(0, 0, 0);position:absolute;top:0;left:0;right:0;bottom:0;padding-top:32px;overflow-y:auto;overflow-x:hidden}google-codelab google-codelab-step[animating],google-codelab google-codelab-step[selected]{display:block;transform-origin:0 50% 0;animation-fill-mode:both}google-codelab google-codelab-step[animating]{pointer-events:none;position:absolute;overflow:hidden}@media (max-width: 768px){google-codelab google-codelab-step{padding-top:8px}} + +/*# sourceMappingURL=google_codelab_scss_bin.css.map */ \ No newline at end of file diff --git a/static/codelab-elements.js b/static/codelab-elements.js new file mode 100755 index 0000000..5252a46 --- /dev/null +++ b/static/codelab-elements.js @@ -0,0 +1,271 @@ +(function(){'use strict';var g="undefined"!=typeof window&&window===this?this:"undefined"!=typeof global&&null!=global?global:this,h="function"==typeof Object.create?Object.create:function(a){function c(){}c.prototype=a;return new c},k;if("function"==typeof Object.setPrototypeOf)k=Object.setPrototypeOf;else{var l;a:{var m={U:!0},n={};try{n.__proto__=m;l=n.U;break a}catch(a){}l=!1}k=l?function(a,c){a.__proto__=c;if(a.__proto__!==c)throw new TypeError(a+" is not extensible");return a}:null}var p=k; +function q(a,c){function b(){}b.prototype=c.prototype;a.Y=c.prototype;a.prototype=new b;a.prototype.constructor=a;a.ea=function(a,b,d){for(var e=Array(arguments.length-2),f=2;f")&&(a=a.replace(z,">"));-1!=a.indexOf('"')&&(a=a.replace(A,"""));-1!=a.indexOf("'")&&(a=a.replace(B,"'"));-1!=a.indexOf("\x00")&&(a=a.replace(C,"�"));return a}var x=/&/g,y=//g,A=/"/g,B=/'/g,C=/\x00/g,v=/[\x00&<>"']/,D=String.prototype.repeat?function(a,c){return a.repeat(c)}:function(a,c){return Array(c+1).join(a)}; +function E(a,c){a=String(a);var b=a.indexOf(".");-1==b&&(b=a.length);return D("0",Math.max(0,c-b))+a};function F(a,c,b,f,e){a=new Date(a,c,b);e=e||0;return a.valueOf()+864E5*(((void 0!==f?f:3)-e+7)%7-((a.getDay()+6)%7-e+7)%7)};function G(){}function H(a){if("number"==typeof a){var c=new G;c.f=a;var b=a;if(0==b)b="Etc/GMT";else{var f=["Etc/GMT",0>b?"-":"+"];b=Math.abs(b);f.push(Math.floor(b/60)%100);b%=60;0!=b&&f.push(":",E(b,2));b=f.join("")}c.g=b;b=a;0==b?b="UTC":(f=["UTC",0>b?"+":"-"],b=Math.abs(b),f.push(Math.floor(b/60)%100),b%=60,0!=b&&f.push(":",b),b=f.join(""));a=I(a);c.i=[b,b];c.a={ba:a,m:a};c.b=[];return c}c=new G;c.g=a.id;c.f=-a.std_offset;c.i=a.names;c.a=a.names_ext;c.b=a.transitions;return c} +function I(a){var c=["GMT"];c.push(0>=a?"+":"-");a=Math.abs(a);c.push(E(Math.floor(a/60)%100,2),":",E(a%60,2));return c.join("")}function J(a,c){c=Date.UTC(c.getUTCFullYear(),c.getUTCMonth(),c.getUTCDate(),c.getUTCHours(),c.getUTCMinutes())/36E5;for(var b=0;b=a.b[b];)b+=2;return 0==b?0:a.b[b-1]};function aa(){this.b=[];this.a=t;var a="MMM d, yyyy";for(ba&&(a=a.replace(/\u200f/g,""));a;){for(var c=a,b=0;b=e?String.fromCharCode(a.T+e-48):c.charAt(f))}c=b.join("")}return c}var ba=!1;function N(a){if(!(a.getHours&&a.getSeconds&&a.getMinutes))throw Error("The date to format has no time (probably a goog.date.Date). Use Date or goog.date.DateTime, or use a pattern without time fields.");} +function ca(a,c,b,f,e){var d=c.length;switch(c.charAt(0)){case "G":return b=0b&&(b=-b),2==d&&(b%=100),M(a,E(b,d));case "Y":return b=(new Date(F(f.getFullYear(),f.getMonth(),f.getDate(),a.a.l,a.a.j))).getFullYear(),0>b&&(b=-b),2==d&&(b%=100),M(a,E(b,d));case "M":a:switch(b=f.getMonth(),d){case 5:d=a.a.G[b];break a;case 4:d=a.a.F[b];break a;case 3:d=a.a.I[b];break a;default:d=M(a,E(b+1,d))}return d;case "k":return N(e), +M(a,E(L(e)||24,d));case "S":return M(a,(e.getMilliseconds()/1E3).toFixed(Math.min(3,d)).substr(2)+(3d?1:0];case "h":return N(e),M(a,E(L(e)%12||12,d));case "K":return N(e),M(a,E(L(e)%12,d));case "H":return N(e),M(a,E(L(e),d));case "c":a:switch(b=f.getDay(),d){case 5:d=a.a.N[b];break a;case 4:d=a.a.R[b];break a;case 3:d=a.a.P[b];break a;default:d=M(a,E(b,1))}return d;case "L":a:switch(b= +f.getMonth(),d){case 5:d=a.a.M[b];break a;case 4:d=a.a.L[b];break a;case 3:d=a.a.O[b];break a;default:d=M(a,E(b+1,d))}return d;case "Q":return b=Math.floor(f.getMonth()/3),4>d?a.a.J[b]:a.a.H[b];case "d":return M(a,E(f.getDate(),d));case "m":return N(e),M(a,E(e.getMinutes(),d));case "s":return N(e),M(a,E(e.getSeconds(),d));case "v":return d=H(b.getTimezoneOffset()),d.g;case "V":return a=H(b.getTimezoneOffset()),2>=d?a.g:0d?a.i[0d?(d=-(c.f-J(c,b)),a=[0>d?"-":"+"],d=Math.abs(d),a.push(E(Math.floor(d/60)%100,2),E(d%60,2)),d=a.join("")):d=M(a,I(c.f-J(c,b))),d;default:return""}};function O(){this.a=P}var P={};var Q={},da={};function R(){throw Error("Do not instantiate directly");}R.prototype.u=null;R.prototype.toString=function(){return this.h};function S(){R.call(this)}q(S,R);S.prototype.c=Q;function ea(a){if(null!=a)switch(a.u){case 1:return 1;case -1:return-1;case 0:return 0}return null}function T(a){return null!=a&&a.c===Q?a:a instanceof O?U(a instanceof O&&a.constructor===O&&a.a===P?"":"type_error:SafeHtml",null):U(u(String(String(a))),ea(a))}var U=function(a){function c(a){this.h=a}c.prototype=a.prototype;return function(a,f){a=new c(String(a));void 0!==f&&(a.u=f);return a}}(S);var fa=function(a){var c=!1,b;return function(){c||(b=a(),c=!0);return b}}(function(){var a=document.createElement("div");a.innerHTML="
";var c=a.firstChild.firstChild;a.innerHTML="";return!c.parentElement});/* + + Copyright 2018 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +function V(){var a=HTMLElement.call(this)||this;a.o="";a.s="";a.v=!1;a.w="";return a}var W=HTMLElement;V.prototype=h(W.prototype);V.prototype.constructor=V;if(p)p(V,W);else for(var X in W)if("prototype"!=X)if(Object.defineProperties){var Y=Object.getOwnPropertyDescriptor(W,X);Y&&Object.defineProperty(V,X,Y)}else V[X]=W[X];V.Y=W.prototype;V.prototype.connectedCallback=function(){this.v||Z(this)};V.prototype.connectedCallback=V.prototype.connectedCallback;V.prototype.disconnectedCallback=function(){}; +V.prototype.disconnectedCallback=V.prototype.disconnectedCallback;V.prototype.attributeChangedCallback=function(a){switch(a){case "last-updated":this.hasAttribute("last-updated")&&(this.w=this.getAttribute("last-updated"));break;case "authors":this.hasAttribute("authors")&&(this.o=this.getAttribute("authors"));break;case "codelab-title":this.hasAttribute("codelab-title")&&(this.s=this.getAttribute("codelab-title"))}Z(this)};V.prototype.attributeChangedCallback=V.prototype.attributeChangedCallback; +function Z(a){var c=a.w;if(c){c=new Date(c);var b=new aa;if(!c)throw Error("The date to format must be non-null.");for(var f=[],e=0;e';d=c.length;for(var w=0;w'+T(c[w])+"";e+=""}e+='

About this codelab

'+ +(b?'
subjectLast updated '+T(b)+"
":"")+'
account_circle'+(f?"Written by "+T(f):"Written by a Googler")+"
";c=U(e);a:{if(c instanceof R){if(c.c===Q){c=c.h;break a}if(c.c===da){c=u(c.h);break a}}c="zSoyz"}if(fa())for(;a.lastChild;)a.removeChild(a.lastChild);a.innerHTML=c;a.v=!0} +g.Object.defineProperties(V,{observedAttributes:{configurable:!0,enumerable:!0,get:function(){return["authors","last-updated","codelab-title"]}}});try{window.customElements.define("google-codelab-about",V)}catch(a){console.warn("googlecodelabs.CodelabAbout",a)};}).call(this); +(function(){'use strict';var aa="function"==typeof Object.defineProperties?Object.defineProperty:function(a,b,c){a!=Array.prototype&&a!=Object.prototype&&(a[b]=c.value)},h="undefined"!=typeof window&&window===this?this:"undefined"!=typeof global&&null!=global?global:this;function ba(){ba=function(){};h.Symbol||(h.Symbol=ca)}var ca=function(){var a=0;return function(b){return"jscomp_symbol_"+(b||"")+a++}}(); +function k(){ba();var a=h.Symbol.iterator;a||(a=h.Symbol.iterator=h.Symbol("iterator"));"function"!=typeof Array.prototype[a]&&aa(Array.prototype,a,{configurable:!0,writable:!0,value:function(){return da(this)}});k=function(){}}function da(a){var b=0;return ea(function(){return b>>0),ta=0;function ua(a,b){function c(){}c.prototype=b.prototype;a.C=b.prototype;a.prototype=new c;a.prototype.constructor=a;a.L=function(a,c,f){for(var d=Array(arguments.length-2),e=2;eb?1:0};var C;a:{var Ca=w.navigator;if(Ca){var Da=Ca.userAgent;if(Da){C=Da;break a}}C=""};function Ea(a,b,c){for(var d in a)b.call(c,a[d],d,a)};function D(a){D[" "](a);return a}D[" "]=x;var Fa=-1!=C.indexOf("Opera"),E=-1!=C.indexOf("Trident")||-1!=C.indexOf("MSIE"),Ga=-1!=C.indexOf("Edge"),Ha=-1!=C.indexOf("Gecko")&&!(-1!=C.toLowerCase().indexOf("webkit")&&-1==C.indexOf("Edge"))&&!(-1!=C.indexOf("Trident")||-1!=C.indexOf("MSIE"))&&-1==C.indexOf("Edge"),Ia=-1!=C.toLowerCase().indexOf("webkit")&&-1==C.indexOf("Edge");function Ja(){var a=w.document;return a?a.documentMode:void 0}var F; +a:{var G="",H=function(){var a=C;if(Ha)return/rv:([^\);]+)(\)|;)/.exec(a);if(Ga)return/Edge\/([\d\.]+)/.exec(a);if(E)return/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(a);if(Ia)return/WebKit\/(\S+)/.exec(a);if(Fa)return/(?:Version)[ \/]?(\S+)/.exec(a)}();H&&(G=H?H[1]:"");if(E){var K=Ja();if(null!=K&&K>parseFloat(G)){F=String(K);break a}}F=G}var L={},Ka;var La=w.document;Ka=La&&E?Ja()||("CSS1Compat"==La.compatMode?parseInt(F,10):5):void 0;var M;(M=!E)||(M=9<=Number(Ka));var Ma=M,N; +if(N=E){var Na;if(Object.prototype.hasOwnProperty.call(L,"9"))Na=L["9"];else{for(var O=0,Oa=Ba(String(F)).split("."),Pa=Ba("9").split("."),Qa=Math.max(Oa.length,Pa.length),P=0;0==O&&P=a.keyCode)a.keyCode=-1}catch(b){}};var Xa="closure_listenable_"+(1E6*Math.random()|0),Ya=0;function Za(a,b,c,d,e){this.listener=a;this.proxy=null;this.src=b;this.type=c;this.capture=!!d;this.a=e;this.key=++Ya;this.m=this.A=!1}function $a(a){a.m=!0;a.listener=null;a.proxy=null;a.src=null;a.a=null};function ab(a){this.src=a;this.a={};this.b=0}ab.prototype.add=function(a,b,c,d,e){var f=a.toString();a=this.a[f];a||(a=this.a[f]=[],this.b++);var g;a:{for(g=0;gd.keyCode||void 0!=d.returnValue)){a:{var e=!1;if(0==d.keyCode)try{d.keyCode=-1;break a}catch(g){e=!0}if(e||void 0==d.returnValue)d.returnValue=!0}d=[];for(e=b.a;e;e=e.parentNode)d.push(e);a=a.type;for(e=d.length-1;0<=e;e--){b.a=d[e];var f=ob(d[e],a,!0,b);c=c&&f}for(e=0;e>>0);function gb(a){if("function"==y(a))return a;a[qb]||(a[qb]=function(b){return a.handleEvent(b)});return a[qb]};function rb(a){ya.call(this);this.b=a;this.a={}}ua(rb,ya);var sb=[];function tb(a,b,c){var d=document.body;"array"!=y(b)&&(b&&(sb[0]=b.toString()),b=sb);for(var e=0;e>>0),ma=0;function na(a,b,c){return a.call.apply(a.bind,arguments)} +function oa(a,b,c){if(!a)throw Error();if(2")&&(a=a.replace(Ea,">"));-1!=a.indexOf('"')&&(a=a.replace(Ha,"""));-1!=a.indexOf("'")&&(a=a.replace(Ia,"'"));-1!=a.indexOf("\x00")&&(a=a.replace(Ja,"�"));return a} +var Ca=/&/g,Da=//g,Ha=/"/g,Ia=/'/g,Ja=/\x00/g,Ba=/[\x00&<>"']/;function Ka(a,b){return ab?1:0};var w;a:{var La=k.navigator;if(La){var Ma=La.userAgent;if(Ma){w=Ma;break a}}w=""};function Na(a){var b=[],c=0,d;for(d in a)b[c++]=d;return b}function x(a){var b={},c;for(c in a)b[c]=a[c];return b};function Oa(a){Oa[" "](a);return a}Oa[" "]=ia;function Pa(a,b){var c=Qa;return Object.prototype.hasOwnProperty.call(c,a)?c[a]:c[a]=b(a)};var Ra=-1!=w.indexOf("Opera"),y=-1!=w.indexOf("Trident")||-1!=w.indexOf("MSIE"),Sa=-1!=w.indexOf("Edge"),Ta=-1!=w.indexOf("Gecko")&&!(-1!=w.toLowerCase().indexOf("webkit")&&-1==w.indexOf("Edge"))&&!(-1!=w.indexOf("Trident")||-1!=w.indexOf("MSIE"))&&-1==w.indexOf("Edge"),Ua=-1!=w.toLowerCase().indexOf("webkit")&&-1==w.indexOf("Edge");function Va(){var a=k.document;return a?a.documentMode:void 0}var z; +a:{var Wa="",Xa=function(){var a=w;if(Ta)return/rv:([^\);]+)(\)|;)/.exec(a);if(Sa)return/Edge\/([\d\.]+)/.exec(a);if(y)return/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(a);if(Ua)return/WebKit\/(\S+)/.exec(a);if(Ra)return/(?:Version)[ \/]?(\S+)/.exec(a)}();Xa&&(Wa=Xa?Xa[1]:"");if(y){var Ya=Va();if(null!=Ya&&Ya>parseFloat(Wa)){z=String(Ya);break a}}z=Wa}var Qa={}; +function Za(a){return Pa(a,function(){for(var b=0,c=v(String(z)).split("."),d=v(String(a)).split("."),e=Math.max(c.length,d.length),f=0;0==b&&f=a.keyCode)a.keyCode=-1}catch(b){}};var gb="closure_listenable_"+(1E6*Math.random()|0),hb=0;function ib(a,b,c,d,e){this.listener=a;this.proxy=null;this.src=b;this.type=c;this.capture=!!d;this.a=e;this.key=++hb;this.l=this.u=!1}function jb(a){a.l=!0;a.listener=null;a.proxy=null;a.src=null;a.a=null};function kb(a){this.src=a;this.a={};this.b=0}kb.prototype.add=function(a,b,c,d,e){var f=a.toString();a=this.a[f];a||(a=this.a[f]=[],this.b++);var g;a:{for(g=0;gd.keyCode||void 0!=d.returnValue)){a:{var e=!1;if(0==d.keyCode)try{d.keyCode=-1;break a}catch(g){e=!0}if(e||void 0==d.returnValue)d.returnValue=!0}d=[];for(e=b.a;e;e=e.parentNode)d.push(e);a=a.type;for(e=d.length-1;0<=e;e--){b.a=d[e];var f=wb(d[e],a,!0,b);c=c&&f}for(e=0;e>>0);function qb(a){if("function"==ja(a))return a;a[yb]||(a[yb]=function(b){return a.handleEvent(b)});return a[yb]};function zb(a){qa.call(this);this.b=a;this.a={}}t(zb,qa);var Ab=[];function Bb(a,b,c){var d="copy";p(d)||(d&&(Ab[0]=d.toString()),d=Ab);for(var e=0;e+~[\]()=^$|]+$/.test(c))throw Error("Selector allows only [-_a-zA-Z0-9#.:* ,>+~[\\]()=^$|] and strings, got: "+a);a:{for(var d={"(":")","[":"]"},e=[],f=0;f",0);I("",0);I("
",0);var dc=function(a){var b=!1,c;return function(){b||(c=a(),b=!0);return c}}(function(){var a=document.createElement("div");a.innerHTML="
";var b=a.firstChild.firstChild;a.innerHTML="";return!b.parentElement});function ec(a,b){if(dc())for(;a.lastChild;)a.removeChild(a.lastChild);a.innerHTML=b}function fc(a,b){ec(a,cc(b))};function gc(a){for(var b;b=a.firstChild;)a.removeChild(b)}function hc(a,b){a.insertBefore(b,a.childNodes[0]||null)}function ic(a){return a&&a.parentNode?a.parentNode.removeChild(a):null}function jc(){this.a=k.document||document}jc.prototype.appendChild=function(a,b){a.appendChild(b)};var kc={"* ARIA-CHECKED":!0,"* ARIA-COLCOUNT":!0,"* ARIA-COLINDEX":!0,"* ARIA-DESCRIBEDBY":!0,"* ARIA-DISABLED":!0,"* ARIA-LABEL":!0,"* ARIA-LABELLEDBY":!0,"* ARIA-READONLY":!0,"* ARIA-REQUIRED":!0,"* ARIA-ROWCOUNT":!0,"* ARIA-ROWINDEX":!0,"* ARIA-SELECTED":!0,"* ABBR":!0,"* ACCEPT":!0,"* ACCESSKEY":!0,"* ALIGN":!0,"* ALT":!0,"* AUTOCOMPLETE":!0,"* AXIS":!0,"* BGCOLOR":!0,"* BORDER":!0,"* CELLPADDING":!0,"* CELLSPACING":!0,"* CHAROFF":!0,"* CHAR":!0,"* CHECKED":!0,"* CLEAR":!0,"* COLOR":!0,"* COLSPAN":!0, +"* COLS":!0,"* COMPACT":!0,"* COORDS":!0,"* DATETIME":!0,"* DIR":!0,"* DISABLED":!0,"* ENCTYPE":!0,"* FACE":!0,"* FRAME":!0,"* HEIGHT":!0,"* HREFLANG":!0,"* HSPACE":!0,"* ISMAP":!0,"* LABEL":!0,"* LANG":!0,"* MAXLENGTH":!0,"* METHOD":!0,"* MULTIPLE":!0,"* NOHREF":!0,"* NOSHADE":!0,"* NOWRAP":!0,"* READONLY":!0,"* REL":!0,"* REV":!0,"* ROLE":!0,"* ROWSPAN":!0,"* ROWS":!0,"* RULES":!0,"* SCOPE":!0,"* SELECTED":!0,"* SHAPE":!0,"* SIZE":!0,"* SPAN":!0,"* START":!0,"* SUMMARY":!0,"* TABINDEX":!0,"* TITLE":!0, +"* TYPE":!0,"* VALIGN":!0,"* VALUE":!0,"* VSPACE":!0,"* WIDTH":!0},lc={"* USEMAP":!0,"* ACTION":!0,"* CITE":!0,"* HREF":!0,"* LONGDESC":!0,"* SRC":!0,"LINK HREF":!0,"* FOR":!0,"* HEADERS":!0,"* NAME":!0,"A TARGET":!0,"* CLASS":!0,"* ID":!0,"* STYLE":!0};var J={}; +function mc(a){if(y&&!Za(9))return[0,0,0,0];var b=J.hasOwnProperty(a)?J[a]:null;if(b)return b;65536~\.\[:]+)/g,1);b=K(b,c,/(\.[^\s\+>~\.\[:]+)/g,2);b=K(b,c,/(::[^\s\+>~\.\[:]+|:first-line|:first-letter|:before|:after)/gi,3);b=K(b,c,/(:[\w-]+\([^\)]*\))/gi,2); +b=K(b,c,/(:[^\s\+>~\.\[:]+)/g,2);b=b.replace(/[\*\s\+>~]/g," ");b=b.replace(/[#\.]/g," ");K(b,c,/([^\s\+>~\.\[:]+)/g,3);b=c;return J[a]=b}function K(a,b,c,d){return a.replace(c,function(a){b[d]+=1;return Array(a.length+1).join(" ")})}function nc(a,b){return a.replace(b,function(a){return Array(a.length+1).join("A")})};function L(a,b){a=k[a];return a&&a.prototype?(b=Object.getOwnPropertyDescriptor(a.prototype,b))&&b.get||null:null}function M(a,b){return(a=k[a])&&a.prototype&&a.prototype[b]||null}var oc=L("Element","attributes")||L("Node","attributes"),pc=M("Element","hasAttribute"),qc=M("Element","getAttribute"),rc=M("Element","setAttribute"),sc=M("Element","removeAttribute");L("Element","innerHTML")||L("HTMLElement","innerHTML"); +var tc=M("Element","getElementsByTagName"),uc=M("Element","matches")||M("Element","msMatchesSelector"),vc=L("Node","nodeName"),wc=L("Node","nodeType"),xc=L("Node","parentNode");L("Node","childNodes");var yc=L("HTMLElement","style")||L("Element","style"),zc=L("HTMLStyleElement","sheet"),Ac=M("CSSStyleDeclaration","getPropertyValue"),Bc=M("CSSStyleDeclaration","setProperty");function N(a,b,c,d){if(a)return a.apply(b);a=b[c];if(!d(a))throw Error("Clobbering detected");return a} +function O(a,b,c,d){if(a)return a.apply(b,d);if(y&&10>document.documentMode){if(!b[c].call)throw Error("IE Clobbering detected");}else if("function"!=typeof b[c])throw Error("Clobbering detected");return b[c].apply(b,d)}function Cc(a){return N(oc,a,"attributes",function(a){return a instanceof NamedNodeMap})}function Dc(a,b,c){try{O(rc,a,"setAttribute",[b,c])}catch(d){if(-1==d.message.indexOf("A security problem occurred"))throw d;}} +function Ec(a){return N(yc,a,"style",function(a){return a instanceof CSSStyleDeclaration})}function Fc(a){return N(zc,a,"sheet",function(a){return a instanceof CSSStyleSheet})}function P(a){return N(vc,a,"nodeName",function(a){return"string"==typeof a})}function T(a){return N(wc,a,"nodeType",function(a){return"number"==typeof a})}function U(a){return N(xc,a,"parentNode",function(a){return!(a&&"string"==typeof a.name&&a.name&&"parentnode"==a.name.toLowerCase())})} +function Gc(a,b){return O(Ac,a,a.getPropertyValue?"getPropertyValue":"getAttribute",[b])||""}function Hc(a,b,c){O(Bc,a,a.setProperty?"setProperty":"setAttribute",[b,c])};var Ic=/[\n\f\r"'()*<>]/g,Jc={"\n":"%0a","\f":"%0c","\r":"%0d",'"':"%22","'":"%27","(":"%28",")":"%29","*":"%2a","<":"%3c",">":"%3e"},Kc=y&&10>document.documentMode?null:/\s*([^\s'",]+[^'",]*(('([^'\r\n\f\\]|\\[^])*')|("([^"\r\n\f\\]|\\[^])*")|[^'",])*)/g;function Lc(a){return Jc[a]||null}var Mc="rgb rgba alpha rect image linear-gradient radial-gradient repeating-linear-gradient repeating-radial-gradient cubic-bezier matrix perspective rotate rotate3d rotatex rotatey steps rotatez scale scale3d scalex scaley scalez skew skewx skewy translate translate3d translatex translatey translatez".split(" "); +function Nc(a,b,c){var d=[];a=Oc(ya(a.cssRules));u(a,function(a){if(b&&!/[a-zA-Z][\w-:\.]*/.test(b))throw Error("Invalid container id");if(!(b&&y&&10==document.documentMode&&/\\['"]/.test(a.selectorText))){var e=b?a.selectorText.replace(Kc,"#"+b+" $1"):a.selectorText;d.push(Xb(e,Pc(a.style,c)))}});return Zb(d)}function Oc(a){return ua(a,function(a){return a instanceof CSSStyleRule||a.type==CSSRule.STYLE_RULE})} +function Qc(a,b,c){a=Rc("");return null==a?ac:Nc(a.sheet,void 0!=b?b:null,c)}function Rc(a){return y&&!Za(10)||"function"!=typeof k.DOMParser?null:(new DOMParser).parseFromString(""+a+"","text/html").body.children[0]} +function Pc(a,b){if(!a)return Ob;for(var c=document.createElement("div").style,d=Sc(a),e=0;eh;h++){var l="\"'".charAt(h);if(g.charAt(0)==l&&g.charAt(g.length-1)==l){g=g.substring(1, +g.length-1);break a}}g=b?(g=b(g,f))&&"about:invalid#zClosurez"!=E(g)?'url("'+E(g).replace(Ic,Lc)+'")':null:null}else g=null;else 0f?1:eb?1:a"+c+""),Tc(c),c=c.innerHTML);var d=document.createElement("template");if("content"in d)d.innerHTML=c,d=d.content;else{var e=document.implementation.createHTMLDocument("x");d=e.body;e.body.innerHTML= +c}c=document.createTreeWalker(d,NodeFilter.SHOW_ELEMENT|NodeFilter.SHOW_TEXT,null,!1);for(d=Vc?new WeakMap:new Xc;e=c.nextNode();){c:{var f=a;var g=e;switch(T(g)){case 3:f=qd(f,g);break c;case 1:if("TEMPLATE"==P(g).toUpperCase())f=null;else{var h=P(g).toUpperCase();if(h in f.f)var l=null;else f.g[h]?l=document.createElement(h):(l=document.createElement("SPAN"),f.w&&Dc(l,"data-sanitizer-original-tag",h.toLowerCase()));if(l){var Q=l,Kb=Cc(g);if(null!=Kb)for(var Lb=0;h=Kb[Lb];Lb++)if(h.specified){var n= +f;var R=g,S=h,B=S.name;if(0==B.lastIndexOf("data-sanitizer-",0))n=null;else{var Mb=P(R);S=S.value;var Fa={tagName:v(Mb).toLowerCase(),attributeName:v(B).toLowerCase()},Ga={K:void 0};"style"==Fa.attributeName&&(Ga.K=Ec(R));R=od(Mb,B);R in n.a?(n=n.a[R],n=n(S,Fa,Ga)):(B=od(null,B),B in n.a?(n=n.a[B],n=n(S,Fa,Ga)):n=null)}null===n||Dc(Q,h.name,n)}f=l}else f=null}break c;default:f=null}}if(f){if(1==T(f)&&d.set(e,f),e=U(e),g=!1,e)h=T(e),l=P(e).toLowerCase(),Q=U(e),11!=h||Q?"body"==l&&Q&&(h=U(Q))&&!U(h)&& +(g=!0):g=!0,h=null,g||!e?h=b:1==T(e)&&(h=d.get(e)),h.content&&(h=h.content),h.appendChild(f)}else gc(e)}d.clear&&d.clear();a=b}else a=document.createElement("SPAN");0")+1,a.lastIndexOf("":">","`":"`","\u0085":"…","\u00a0":" ","\u2028":"
","\u2029":"
"};function yd(a){return xd[a]} +var zd=/[\x00\x22\x27\x3c\x3e]/g,Ad=/<(?:!|\/?([a-zA-Z][a-zA-Z0-9:\-]*))(?:[^>'"]|"[^"]*"|'[^']*')*>/g,Bd=/'+vd(a+1)+". "+vd(b)+"");a=(sa||(sa=new jc)).a.createElement("DIV");b:{if(b instanceof W){if(b.m===rd){b=b.content;break b}if(b.m===sd){b=Aa(b.content);break b}}b="zSoyz"}ec(a,b);1==a.childNodes.length&&(b=a.firstChild,1==b.nodeType&&(a=b));return a}var Dd={};/* + + Copyright 2018 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +function X(){var a=HTMLElement.call(this)||this;a.o=null;a.h=null;a.G=!1;a.D=0;a.C="";a.s=null;a.i=null;a.L=new zb;return a}var Y=HTMLElement;X.prototype=ba(Y.prototype);X.prototype.constructor=X;if(ha)ha(X,Y);else for(var Z in Y)if("prototype"!=Z)if(Object.defineProperties){var Ed=Object.getOwnPropertyDescriptor(Y,Z);Ed&&Object.defineProperty(X,Z,Ed)}else X[Z]=Y[Z];X.H=Y.prototype;X.prototype.connectedCallback=function(){Fd(this)};X.prototype.connectedCallback=X.prototype.connectedCallback; +X.prototype.disconnectedCallback=function(){};X.prototype.disconnectedCallback=X.prototype.disconnectedCallback;X.prototype.attributeChangedCallback=function(a){if("label"===a||"step"===a)if(this.hasAttribute("label")&&(this.C=this.getAttribute("label")),this.hasAttribute("step")&&(this.D=parseInt(this.getAttribute("step")||"",10)),this.s){a=Cd({step:this.D,label:this.C});var b=this.s,c=b.parentNode;c&&c.replaceChild(a,b);this.s=a}};X.prototype.attributeChangedCallback=X.prototype.attributeChangedCallback; +function Fd(a){if(!a.G){a.setAttribute("tabindex","-1");var b=a.getElementsByTagName("google-codelab-about");0>>0),fa=0;function t(a,b){function c(){}c.prototype=b.prototype;a.A=b.prototype;a.prototype=new c;a.prototype.constructor=a;a.G=function(a,c,f){for(var d=Array(arguments.length-2),e=2;e")&&(a=a.replace(pa,">"));-1!=a.indexOf('"')&&(a=a.replace(qa,"""));-1!=a.indexOf("'")&&(a=a.replace(ra,"'"));-1!=a.indexOf("\x00")&&(a=a.replace(sa,"�"));return a} +var na=/&/g,oa=//g,qa=/"/g,ra=/'/g,sa=/\x00/g,ma=/[\x00&<>"']/;function v(a,b){return ab?1:0};var w;a:{var ta=m.navigator;if(ta){var ua=ta.userAgent;if(ua){w=ua;break a}}w=""};function va(a,b,c){for(var d in a)b.call(c,a[d],d,a)};function x(a){x[" "](a);return a}x[" "]=p;var wa=-1!=w.indexOf("Opera"),y=-1!=w.indexOf("Trident")||-1!=w.indexOf("MSIE"),xa=-1!=w.indexOf("Edge"),ya=-1!=w.indexOf("Gecko")&&!(-1!=w.toLowerCase().indexOf("webkit")&&-1==w.indexOf("Edge"))&&!(-1!=w.indexOf("Trident")||-1!=w.indexOf("MSIE"))&&-1==w.indexOf("Edge"),za=-1!=w.toLowerCase().indexOf("webkit")&&-1==w.indexOf("Edge");function Aa(){var a=m.document;return a?a.documentMode:void 0}var z; +a:{var A="",B=function(){var a=w;if(ya)return/rv:([^\);]+)(\)|;)/.exec(a);if(xa)return/Edge\/([\d\.]+)/.exec(a);if(y)return/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(a);if(za)return/WebKit\/(\S+)/.exec(a);if(wa)return/(?:Version)[ \/]?(\S+)/.exec(a)}();B&&(A=B?B[1]:"");if(y){var C=Aa();if(null!=C&&C>parseFloat(A)){z=String(C);break a}}z=A}var D={},Ba;var Ca=m.document;Ba=Ca&&y?Aa()||("CSS1Compat"==Ca.compatMode?parseInt(z,10):5):void 0;var E;(E=!y)||(E=9<=Number(Ba));var Da=E,G; +if(G=y){var H;if(Object.prototype.hasOwnProperty.call(D,"9"))H=D["9"];else{for(var I=0,Ea=la(String(z)).split("."),Fa=la("9").split("."),Ga=Math.max(Ea.length,Fa.length),J=0;0==I&&J=a.keyCode)a.keyCode=-1}catch(b){}};var O="closure_listenable_"+(1E6*Math.random()|0),Ma=0;function Na(a,b,c,d,e){this.listener=a;this.proxy=null;this.src=b;this.type=c;this.capture=!!d;this.a=e;this.key=++Ma;this.i=this.l=!1}function Oa(a){a.i=!0;a.listener=null;a.proxy=null;a.src=null;a.a=null};function P(a){this.src=a;this.a={};this.b=0}P.prototype.add=function(a,b,c,d,e){var f=a.toString();a=this.a[f];a||(a=this.a[f]=[],this.b++);var g;a:{for(g=0;gd.keyCode||void 0!=d.returnValue)){a:{var e=!1;if(0==d.keyCode)try{d.keyCode=-1;break a}catch(g){e=!0}if(e||void 0==d.returnValue)d.returnValue=!0}d=[];for(e=b.a;e;e=e.parentNode)d.push(e);a=a.type;for(e=d.length-1;0<=e;e--){b.a=d[e];var f=Ya(d[e],a,!0,b);c=c&&f}for(e=0;e>>0);function Sa(a){if("function"==q(a))return a;a[$a]||(a[$a]=function(b){return a.handleEvent(b)});return a[$a]};function ab(a){ha.call(this);this.b=a;this.a={}}t(ab,ha);var bb=[];function cb(a,b,c){var d="change";"array"!=q(d)&&(d&&(bb[0]=d.toString()),d=bb);for(var e=0;e":">","`":"`","\u0085":"…","\u00a0":" ","\u2028":"
","\u2029":"
"};function rb(a){return sb[a]} +var qb=/[\x00\x22\x27\x3c\x3e]/g,tb=/[\x00\x09-\x0d \x22\x26\x27\x2d\/\x3c-\x3e`\x85\xa0\u2028\u2029]/g,ub=/[\x00\x09-\x0d \x22\x27\x2d\/\x3c-\x3e`\x85\xa0\u2028\u2029]/g,ob=/<(?:!|\/?([a-zA-Z][a-zA-Z0-9:\-]*))(?:[^>'"]|"[^"]*"|'[^']*')*>/g,pb=/
";var b=a.firstChild.firstChild;a.innerHTML="";return!b.parentElement});function wb(a){a&&a.parentNode&&a.parentNode.removeChild(a)}function xb(a){return yb(a,function(a){var b;if(b=n(a.className))a=a.className.split(/\s+/),b=0<=ka(a,"survey-option-wrapper");return b})}function yb(a,b){for(var c=0;a;){if(b(a))return a;a=a.parentNode;c++}return null}function zb(){this.a=m.document||document};function Ab(a){var b=a||Bb;a=b.F;b=b.D;b='

'+mb(e.v)+"

";if(e.options.length){b+='
';for(var f=e.options,g=f.length,l=0;l'+mb(F.C)+''}b+="
"}b+="
"}b=nb(b+"");a=(ja||(ja=new zb)).a.createElement("DIV");b:{if(b instanceof V){if(b.g===U){b=b.h;break b}if(b.g===jb){b=u(b.h);break b}}b="zSoyz"}if(vb())for(;a.lastChild;)a.removeChild(a.lastChild);a.innerHTML=b;1==a.childNodes.length&&(b=a.firstChild,1==b.nodeType&&(a=b));return a}var Bb={};/* + + Copyright 2018 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +function X(){var a=HTMLElement.call(this)||this;a.f=a.getAttribute("survey-id")||"default-codelabs-survey";a.c=new gb;a.w="codelab-survey-"+a.f;a.j={};a.u=new ab;return a}var Y=HTMLElement;X.prototype=aa(Y.prototype);X.prototype.constructor=X;if(da)da(X,Y);else for(var Z in Y)if("prototype"!=Z)if(Object.defineProperties){var Cb=Object.getOwnPropertyDescriptor(Y,Z);Cb&&Object.defineProperty(X,Z,Cb)}else X[Z]=Y[Z];X.A=Y.prototype; +X.prototype.connectedCallback=function(){var a=this.c.get(this.w);this.j[this.f]=a?JSON.parse(a):{};Db(this);Eb(this)};X.prototype.connectedCallback=X.prototype.connectedCallback; +function Eb(a){cb(a.u,a,function(b){if(b.target instanceof HTMLInputElement){b=b.target;var c=xb(b);if(c instanceof Element){var d=c.querySelector(".option-text");c="";d&&(c=d.textContent);b=b.name;a.j[a.f][b]=c;a.c.set(a.w,JSON.stringify(a.j[a.f]));b=new CustomEvent("google-codelab-action",{detail:{category:"survey",action:b.substring(0,500),label:c.substring(0,500)}});document.body.dispatchEvent(b)}}})} +function Db(a){var b=a.querySelectorAll("paper-radio-group"),c=a.querySelectorAll("h4"),d=[];b.length&&c.length==b.length&&(b.forEach(function(a,b){var e=[],f=a.querySelectorAll("paper-radio-button");wb(a);f.forEach(function(a){a=a.textContent;e.push({o:Fb(c[b].textContent,a),C:a})});d.push({v:c[b].textContent,options:e});wb(c[b])}),b=Ab({D:a.f,F:d}),a.appendChild(b));Gb(a);a.setAttribute("upgraded","")} +function Gb(a){var b=a.j[a.f];b&&Object.keys(b).forEach(function(c){if(c=a.querySelector("#"+Fb(c,b[c])))c.checked=!0})}function Fb(a,b){return(a+"--"+b).replace(/\s+/g,"-").replace(/[^a-zA-Z0-9 \-]/g,"").toLowerCase()}X.prototype.disconnectedCallback=function(){db(this.u)};X.prototype.disconnectedCallback=X.prototype.disconnectedCallback;try{window.customElements.define("google-codelab-survey",X)}catch(a){console.warn("googlecodelabs.CodelabSurvey",a)};}).call(this); +(function(){'use strict';var aa="undefined"!=typeof window&&window===this?this:"undefined"!=typeof global&&null!=global?global:this,ba="function"==typeof Object.create?Object.create:function(a){function b(){}b.prototype=a;return new b},ca;if("function"==typeof Object.setPrototypeOf)ca=Object.setPrototypeOf;else{var da;a:{var ea={ba:!0},fa={};try{fa.__proto__=ea;da=fa.ba;break a}catch(a){}da=!1}ca=da?function(a,b){a.__proto__=b;if(a.__proto__!==b)throw new TypeError(a+" is not extensible");return a}:null} +var ha=ca,h=this;function l(a){return"string"==typeof a}function ia(){} +function ja(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null"; +else if("function"==b&&"undefined"==typeof a.call)return"object";return b}function m(a){return"array"==ja(a)}function n(a){var b=typeof a;return"object"==b&&null!=a||"function"==b}var ka="closure_uid_"+(1E9*Math.random()>>>0),la=0;function ma(a,b,c){return a.call.apply(a.bind,arguments)} +function na(a,b,c){if(!a)throw Error();if(2")&&(a=a.replace(ya,">"));-1!=a.indexOf('"')&&(a=a.replace(za,"""));-1!=a.indexOf("'")&&(a=a.replace(Aa,"'"));-1!=a.indexOf("\x00")&&(a=a.replace(Ba,"�"));return a} +var wa=/&/g,xa=//g,za=/"/g,Aa=/'/g,Ba=/\x00/g,va=/[\x00&<>"']/;function Ca(a,b){return ab?1:0}function Da(a){return String(a).replace(/\-([a-z])/g,function(a,c){return c.toUpperCase()})}function Ea(a){var b=l(void 0)?"undefined".replace(/([-()\[\]{}+?*.$\^|,:#parseFloat(Qa)){A=String(Sa);break a}}A=Qa}var Ma={}; +function Ta(a){return La(a,function(){for(var b=0,c=ua(String(A)).split("."),d=ua(String(a)).split("."),e=Math.max(c.length,d.length),f=0;0==b&&f=a.keyCode)a.keyCode=-1}catch(b){}};var D="closure_listenable_"+(1E6*Math.random()|0),ab=0;function bb(a,b,c,d,e){this.listener=a;this.proxy=null;this.src=b;this.type=c;this.capture=!!d;this.M=e;this.key=++ab;this.C=this.H=!1}function cb(a){a.C=!0;a.listener=null;a.proxy=null;a.src=null;a.M=null};function E(a){this.src=a;this.a={};this.b=0}E.prototype.add=function(a,b,c,d,e){var f=a.toString();a=this.a[f];a||(a=this.a[f]=[],this.b++);var g=db(a,b,d,e);-1d.keyCode||void 0!=d.returnValue)){a:{var e=!1;if(0==d.keyCode)try{d.keyCode=-1;break a}catch(g){e=!0}if(e||void 0==d.returnValue)d.returnValue=!0}d=[];for(e=b.a;e;e=e.parentNode)d.push(e);a=a.type;for(e=d.length-1;!b.c&&0<=e;e--){b.a=d[e];var f=rb(d[e],a,!0,b);c=c&&f}for(e=0;!b.c&&e>>0);function kb(a){if("function"==ja(a))return a;a[tb]||(a[tb]=function(b){return a.handleEvent(b)});return a[tb]};function G(a){r.call(this);this.b=a;this.a={}}q(G,r);var ub=[];function H(a,b,c,d){m(c)||(c&&(ub[0]=c.toString()),c=ub);for(var e=0;e",0);P("",0);P("
",0);var ac={},bc={},cc={},dc={};function Q(){throw Error("Do not instantiate directly");}Q.prototype.W=null;Q.prototype.toString=function(){return this.L};function ec(){Q.call(this)}q(ec,Q);ec.prototype.v=ac;function fc(a){if(null!=a)switch(a.W){case 1:return 1;case -1:return-1;case 0:return 0}return null}function gc(a){return null!=a&&a.v===ac?a:a instanceof N?R(O(a),a.w()):R(t(String(String(a))),fc(a))}var R=function(a){function b(a){this.L=a}b.prototype=a.prototype;return function(a,d){a=new b(String(a));void 0!==d&&(a.W=d);return a}}(ec);function S(a){return null!=a&&a.v===ac?String(String(a.L).replace(hc,"").replace(ic,"<")).replace(jc,kc):t(String(a))} +function lc(a){null!=a&&a.v===bc||null!=a&&a.v===cc?a=String(a).replace(mc,nc):a instanceof L?a=String(Ib(a)).replace(mc,nc):a instanceof K?a=String(Gb(a)).replace(mc,nc):(a=String(a),a=oc.test(a)?a.replace(mc,nc):"about:invalid#zSoyz");return a} +var pc={"\x00":"�","\t":" ","\n":" ","\x0B":" ","\f":" ","\r":" "," ":" ",'"':""","&":"&","'":"'","-":"-","/":"/","<":"<","=":"=",">":">","`":"`","\u0085":"…","\u00a0":" ","\u2028":"
","\u2029":"
"};function kc(a){return pc[a]} +var qc={"\x00":"%00","\u0001":"%01","\u0002":"%02","\u0003":"%03","\u0004":"%04","\u0005":"%05","\u0006":"%06","\u0007":"%07","\b":"%08","\t":"%09","\n":"%0A","\x0B":"%0B","\f":"%0C","\r":"%0D","\u000e":"%0E","\u000f":"%0F","\u0010":"%10","\u0011":"%11","\u0012":"%12","\u0013":"%13","\u0014":"%14","\u0015":"%15","\u0016":"%16","\u0017":"%17","\u0018":"%18","\u0019":"%19","\u001a":"%1A","\u001b":"%1B","\u001c":"%1C","\u001d":"%1D","\u001e":"%1E","\u001f":"%1F"," ":"%20",'"':"%22","'":"%27","(":"%28", +")":"%29","<":"%3C",">":"%3E","\\":"%5C","{":"%7B","}":"%7D","\u007f":"%7F","\u0085":"%C2%85","\u00a0":"%C2%A0","\u2028":"%E2%80%A8","\u2029":"%E2%80%A9","\uff01":"%EF%BC%81","\uff03":"%EF%BC%83","\uff04":"%EF%BC%84","\uff06":"%EF%BC%86","\uff07":"%EF%BC%87","\uff08":"%EF%BC%88","\uff09":"%EF%BC%89","\uff0a":"%EF%BC%8A","\uff0b":"%EF%BC%8B","\uff0c":"%EF%BC%8C","\uff0f":"%EF%BC%8F","\uff1a":"%EF%BC%9A","\uff1b":"%EF%BC%9B","\uff1d":"%EF%BC%9D","\uff1f":"%EF%BC%9F","\uff20":"%EF%BC%A0","\uff3b":"%EF%BC%BB", +"\uff3d":"%EF%BC%BD"};function nc(a){return qc[a]}var jc=/[\x00\x22\x27\x3c\x3e]/g,mc=/[\x00- \x22\x27-\x29\x3c\x3e\\\x7b\x7d\x7f\x85\xa0\u2028\u2029\uff01\uff03\uff04\uff06-\uff0c\uff0f\uff1a\uff1b\uff1d\uff1f\uff20\uff3b\uff3d]/g,oc=/^(?![^#?]*\/(?:\.|%2E){2}(?:[\/?#]|$))(?:(?:https?|mailto):|[^&:\/?#]*(?:[\/?#]|$))/i,hc=/<(?:!|\/?([a-zA-Z][a-zA-Z0-9:\-]*))(?:[^>'"]|"[^"]*"|'[^']*')*>/g,ic=/
')}function sc(a){var b=a.title;return R('

'+gc(b)+"

")} +function tc(a){a=a.time;return R('
access_time'+(1==a?gc(a)+" min remaining":gc(a)+" mins remaining")+"
")} +function uc(a){a=a.f;for(var b='
    ',c=a.length,d=0;d'+gc(e)+""}return R(b+"
")};function T(){r.call(this);this.b=new E(this);this.c=this}q(T,r);T.prototype[D]=!0;T.prototype.removeEventListener=function(a,b,c,d){pb(this,a,b,c,d)};function vc(a,b,c,d){b=a.b.a[String(b)];if(!b)return!0;b=b.concat();for(var e=!0,f=0;f
";var b=a.firstChild.firstChild;a.innerHTML="";return!b.parentElement});function Ac(a,b){if(zc())for(;a.lastChild;)a.removeChild(a.lastChild);a.innerHTML=b};function Bc(a,b){for(;a&&1!=a.nodeType;)a=b?a.nextSibling:a.previousSibling;return a}function Cc(){this.a=h.document||document};function V(a,b,c){if(l(b))(b=Dc(a,b))&&(a.style[b]=c);else for(var d in b){c=a;var e=b[d],f=Dc(c,d);f&&(c.style[f]=e)}}var Ec={};function Dc(a,b){var c=Ec[b];if(!c){var d=Da(b);c=d;void 0===a.style[d]&&(d=(z?"Webkit":w?"Moz":v?"ms":Na?"O":null)+Ea(d),void 0!==a.style[d]&&(c=d));Ec[b]=c}return c} +function Fc(a){var b=a.offsetWidth,c=a.offsetHeight;c=z&&!b&&!c;if((void 0===b||c)&&a.getBoundingClientRect)a:{try{var d=a.getBoundingClientRect()}catch(e){break a}v&&a.ownerDocument.body&&(a=a.ownerDocument,d.left-=a.documentElement.clientLeft+a.body.clientLeft,d.top-=a.documentElement.clientTop+a.body.clientTop)}};function Gc(a,b){m(b)||(b=[b]);b=ta(b,function(a){return l(a)?a:a.ia+" "+a.duration+"s "+a.timing+" "+a.ca+"s"});V(a,"transition",b.join(","))} +var Hc=yb(function(){if(v)return Ta("10.0");var a=document.createElement("DIV");var b=z?"-webkit":w?"-moz":v?"-ms":Na?"-o":null,c={transition:"opacity 1s linear"};b&&(c[b+"-transition"]="opacity 1s linear");b={style:c};if(!Xb.test("div"))throw Error("Invalid tag name
.");if("DIV"in Zb)throw Error("Tag name
is not allowed for SafeHtml.");c=null;var d="";if(b)for(y in b){if(!Xb.test(y))throw Error('Invalid attribute name "'+y+'".');var e=b[y];if(null!=e){var f=y;var g=e;if(g instanceof J)g= +Eb(g);else if("style"==f.toLowerCase()){e=void 0;if(!n(g))throw Error('The "style" attribute requires goog.html.SafeStyle or map of style properties, '+typeof g+" given: "+g);if(!(g instanceof M)){var k="";for(e in g){if(!/^[-_a-zA-Z0-9]+$/.test(e))throw Error("Name allows only [-_a-zA-Z0-9], got: "+e);var x=g[e];null!=x&&(x=m(x)?ta(x,Pb).join(" "):Pb(x),k+=e+":"+x+";")}g=k?Nb(k):Ob}g=g instanceof M&&g.constructor===M&&g.b===Mb?g.a:"type_error:SafeStyle"}else{if(/^on/i.test(f))throw Error('Attribute "'+ +f+'" requires goog.string.Const value, "'+g+'" given.');if(f.toLowerCase()in Yb)if(g instanceof K)g=Gb(g);else if(g instanceof L)g=Ib(g);else if(l(g))g=Kb(g).l();else throw Error('Attribute "'+f+'" on tag "div" requires goog.html.SafeUrl, goog.string.Const, or string, value "'+g+'" given.');}g.s&&(g=g.l());f=f+'="'+t(String(g))+'"';d+=" "+f}}var y="":(c=$b(d),y+=">"+O(c)+"
",c=c.w());(b=b&&b.dir)&&(/^(ltr|rtl|auto)$/i.test(b)?c=0:c= +null);b=P(y,c);Ac(a,O(b));a=a.firstChild;b=a.style[Da("transition")];return""!=("undefined"!==typeof b?b:a.style[Dc(a,"transition")]||"")});function W(a,b,c,d,e){xc.call(this);this.a=a;this.fa=b;this.ga=c;this.X=d;this.ja=m(e)?e:[e]}q(W,xc);function Ic(a){1!=a.K&&(U(a.c,"begin"),U(a.c,"play"),oa(),a.K=1,Hc()?(V(a.a,a.ga),a.Y=wc(a.ha,void 0,a)):a.P(!1))} +W.prototype.ha=function(){var a=this.a;b:{var b=9==a.nodeType?a:a.ownerDocument||a.document;if(b.defaultView&&b.defaultView.getComputedStyle&&(b=b.defaultView.getComputedStyle(a,null))){b=b.display||b.getPropertyValue("display")||"";break b}b=""}if("none"!=(b||(a.currentStyle?a.currentStyle.display:null)||a.style&&a.style.display))Fc(a);else{b=a.style;var c=b.display,d=b.visibility,e=b.position;b.visibility="hidden";b.position="absolute";b.display="inline";Fc(a);b.display=c;b.position=e;b.visibility= +d}Gc(this.a,this.ja);V(this.a,this.X);this.Y=wc(p(this.P,this,!1),1E3*this.fa)};function Jc(a){1==a.K&&a.P(!0)}W.prototype.P=function(a){V(this.a,"transition","");h.clearTimeout(this.Y);V(this.a,this.X);oa();this.K=yc;a?U(this.c,"stop"):U(this.c,"finish");U(this.c,"end")};function Kc(a,b,c){Ac(a,Lc(b(c||Mc,void 0,void 0)))}function Nc(a,b){b=a(b||Mc,void 0,void 0);a=(qa||(qa=new Cc)).a.createElement("DIV");b=Lc(b);Ac(a,b);1==a.childNodes.length&&(b=a.firstChild,1==b.nodeType&&(a=b));return a}function Lc(a){if(a instanceof Q){if(a.v===ac)return a.L;if(a.v===dc)return t(a.L)}return"zSoyz"}var Mc={};/* + + Copyright 2018 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +function X(){var a=HTMLElement.call(this)||this;a.h=null;a.aa=null;a.N=null;a.m=null;a.B=null;a.j=null;a.I=null;a.J=null;a.A="";a.u="";a.$=-1;a.f=[];a.g=-1;a.i=new G;a.O=new G;a.S=!1;a.F=null;a.G=null;a.o=new Bb;return a}var Y=HTMLElement;X.prototype=ba(Y.prototype);X.prototype.constructor=X;if(ha)ha(X,Y);else for(var Z in Y)if("prototype"!=Z)if(Object.defineProperties){var Oc=Object.getOwnPropertyDescriptor(Y,Z);Oc&&Object.defineProperty(X,Z,Oc)}else X[Z]=Y[Z];X.V=Y.prototype; +X.prototype.connectedCallback=function(){this.A=this.getAttribute("id");var a;(a=Pc(this.hash))||(a=parseInt(this.o.get("progress_"+this.A),10),a=!isNaN(a)&&a?a:0);this.setAttribute("selected",""+a);Qc(this);Rc(this);Sc(this);this.S||(this.S=!0,Tc(this,"google-codelab-ready"),this.setAttribute("google-codelab-ready",""))};X.prototype.connectedCallback=X.prototype.connectedCallback;X.prototype.disconnectedCallback=function(){xb(this.i);xb(this.O)};X.prototype.disconnectedCallback=X.prototype.disconnectedCallback; +X.prototype.attributeChangedCallback=function(a){var b=this;switch(a){case "title":this.hasAttribute("title")&&(this.u=this.getAttribute("title"),this.removeAttribute("title"),this.setAttribute("codelab-title",this.u));break;case "codelab-title":this.u=this.getAttribute("codelab-title");Uc(this);break;case "selected":Vc(this);Sc(this);break;case "no-toolbar":Wc(this);break;case "no-arrows":Xc(this);break;case "anayltics-ready":this.hasAttribute("anayltics-ready")&&(this.S?Yc(this):H(this.i,this,"google-codelab-ready", +function(){return Yc(b)}))}};X.prototype.attributeChangedCallback=X.prototype.attributeChangedCallback;function Zc(a){var b=document.querySelector("google-codelab-analytics");if(b){var c=a.getAttribute("codelab-gaid");c&&b.setAttribute("codelab-gaid",c);(c=a.getAttribute("codelab-ga4id"))&&b.setAttribute("codelab-ga4id",c);a.A&&b.setAttribute("codelab-id",a.A);b.setAttribute("environment",a.getAttribute("environment"));b.setAttribute("category",a.getAttribute("category"))}} +X.prototype.T=function(){this.setAttribute("selected",this.g+1)};X.prototype.selectNext=X.prototype.T;X.prototype.U=function(){this.setAttribute("selected",this.g-1)};X.prototype.selectPrevious=X.prototype.U;X.prototype.select=function(a){this.setAttribute("selected",a)};X.prototype.select=X.prototype.select; +function Rc(a){a.j&&H(a.i,a.j,"click",function(b){b.preventDefault();b.stopPropagation();a.U()});a.B&&H(a.i,a.B,"click",function(b){b.preventDefault();b.stopPropagation();a.T()});a.h&&(H(a.i,a.h,"click",function(b){for(var c=b.target;c!==a.h&&"A"!==c.tagName.toUpperCase();)b.preventDefault(),b.stopPropagation(),c=c.parentNode;c!==a.h&&(b=(new URL(c.getAttribute("href"),document.location.origin)).hash,a.setAttribute("selected",""+Pc(b)))}),H(a.i,a.h,"keydown",function(b){if(a.h){var c=a.h.querySelector(":focus"), +e;c?e=c.parentNode:e=a.h.querySelector("[selected]");if(e){var f;38==b.D?f=void 0!==e.previousElementSibling?e.previousElementSibling:Bc(e.previousSibling,!1):40==b.D&&(f=void 0!==e.nextElementSibling?e.nextElementSibling:Bc(e.nextSibling,!0));f&&(b=f.querySelector("a"))&&b.focus()}}}));if(a.m){var b=a.m.querySelector("#menu");b&&(H(a.i,b,"click",function(b){b.preventDefault();b.stopPropagation();a.hasAttribute("drawer--open")?a.removeAttribute("drawer--open"):a.setAttribute("drawer--open","")}), +H(a.i,document.body,"click",function(){a.hasAttribute("drawer--open")&&a.removeAttribute("drawer--open")}))}H(a.i,document.body,"keydown",function(b){37==b.D?(document.activeElement&&document.activeElement.blur(),a.U()):39==b.D&&(document.activeElement&&document.activeElement.blur(),a.T())});(b=a.querySelector("#codelab-feedback"))&&H(a.i,b,"click",function(a){"userfeedback"in window&&(window.userfeedback.api.startFeedback({productId:"5143948"}),a.preventDefault())})} +function Wc(a){a.m&&(a.hasAttribute("no-toolbar")?a.m.setAttribute("hidden",""):a.m.removeAttribute("hidden"))}function Xc(a){a.I&&(a.hasAttribute("no-arrows")?a.I.setAttribute("hidden",""):a.I.removeAttribute("hidden"))} +function Uc(a){if(a.u&&a.m){var b=new URL(document.location.href);b.hash="";b=Nc(sc,{title:a.u,url:b.href});document.title=a.u;var c=a.m.querySelector("h1");a=a.m.querySelector("#codelab-nav-buttons");c?(a=c.parentNode)&&a.replaceChild(b,c):a.parentNode&&a.parentNode.insertBefore(b,a.nextSibling)}} +function $c(a){if(a.N&&a.N.length){for(var b=0,c=a.g;c>>0),ma=0;function na(a,b,c){return a.call.apply(a.bind,arguments)} +function oa(a,b,c){if(!a)throw Error();if(2")&&(a=a.replace(za,">"));-1!=a.indexOf('"')&&(a=a.replace(Aa,"""));-1!=a.indexOf("'")&&(a=a.replace(Ba,"'"));-1!=a.indexOf("\x00")&&(a=a.replace(Ca,"�"));return a} +var xa=/&/g,ya=//g,Aa=/"/g,Ba=/'/g,Ca=/\x00/g,wa=/[\x00&<>"']/;function A(a,b){return ab?1:0};function Da(a,b,c){for(var d in a)b.call(c,a[d],d,a)};function B(){this.a="";this.v=Ea}B.prototype.i=!0;B.prototype.c=function(){return this.a};B.prototype.b=function(){return 1};var Fa=/^(?:(?:https?|mailto|ftp):|[^:/?#]*(?:[/?#]|$))/i,Ea={};function Ga(a){var b=new B;b.a=a;return b}Ga("about:blank");var C;a:{var Ha=q.navigator;if(Ha){var Ia=Ha.userAgent;if(Ia){C=Ia;break a}}C=""};function D(){this.a=Ja}D.prototype.b=function(){return null};D.prototype.i=!0;D.prototype.c=function(){return""};var Ja={};var E={},Ka={};function F(){throw Error("Do not instantiate directly");}F.prototype.B=null;F.prototype.toString=function(){return this.u};function G(){F.call(this)}x(G,F);G.prototype.s=E;function H(a){H[" "](a);return a}H[" "]=t;var La=-1!=C.indexOf("Opera"),I=-1!=C.indexOf("Trident")||-1!=C.indexOf("MSIE"),Ma=-1!=C.indexOf("Edge"),Na=-1!=C.indexOf("Gecko")&&!(-1!=C.toLowerCase().indexOf("webkit")&&-1==C.indexOf("Edge"))&&!(-1!=C.indexOf("Trident")||-1!=C.indexOf("MSIE"))&&-1==C.indexOf("Edge"),Oa=-1!=C.toLowerCase().indexOf("webkit")&&-1==C.indexOf("Edge");function Sa(){var a=q.document;return a?a.documentMode:void 0}var J; +a:{var K="",L=function(){var a=C;if(Na)return/rv:([^\);]+)(\)|;)/.exec(a);if(Ma)return/Edge\/([\d\.]+)/.exec(a);if(I)return/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(a);if(Oa)return/WebKit\/(\S+)/.exec(a);if(La)return/(?:Version)[ \/]?(\S+)/.exec(a)}();L&&(K=L?L[1]:"");if(I){var Ta=Sa();if(null!=Ta&&Ta>parseFloat(K)){J=String(Ta);break a}}J=K}var Ua={},Va;var Wa=q.document;Va=Wa&&I?Sa()||("CSS1Compat"==Wa.compatMode?parseInt(J,10):5):void 0;function Xa(a){if(null!=a)switch(a.B){case 1:return 1;case -1:return-1;case 0:return 0}return null}function M(a){return null!=a&&a.s===E?a:a instanceof D?N(a instanceof D&&a.constructor===D&&a.a===Ja?"":"type_error:SafeHtml",a.b()):N(z(String(String(a))),Xa(a))}var N=function(a){function b(a){this.u=a}b.prototype=a.prototype;return function(a,d){a=new b(String(a));void 0!==d&&(a.B=d);return a}}(G); +function O(a){return null!=a&&a.s===E?String(String(a.u).replace(Ya,"").replace(Za,"<")).replace($a,ab):z(String(a))}var bb={"\x00":"�","\t":" ","\n":" ","\x0B":" ","\f":" ","\r":" "," ":" ",'"':""","&":"&","'":"'","-":"-","/":"/","<":"<","=":"=",">":">","`":"`","\u0085":"…","\u00a0":" ","\u2028":"
","\u2029":"
"};function ab(a){return bb[a]} +var $a=/[\x00\x22\x27\x3c\x3e]/g,Ya=/<(?:!|\/?([a-zA-Z][a-zA-Z0-9:\-]*))(?:[^>'"]|"[^"]*"|'[^']*')*>/g,Za=/A-ZRecentDuration
")};var db=function(a){var b=!1,c;return function(){b||(c=a(),b=!0);return c}}(function(){var a=document.createElement("div");a.innerHTML="
";var b=a.firstChild.firstChild;a.innerHTML="";return!b.parentElement});function eb(a,b){if(db())for(;a.lastChild;)a.removeChild(a.lastChild);a.innerHTML=b};function fb(){this.a=q.document||document};function gb(a,b){b=a(b||hb,void 0,void 0);a=(qa||(qa=new fb)).a.createElement("DIV");b=ib(b);eb(a,b);1==a.childNodes.length&&(b=a.firstChild,1==b.nodeType&&(a=b));return a}function ib(a){if(a instanceof F){if(a.s===E)return a.u;if(a.s===Ka)return z(a.u)}return"zSoyz"}var hb={};/* + + Copyright 2018 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +function P(){var a=HTMLElement.call(this)||this;a.j=new ua;return a}ea(P,HTMLElement);P.prototype.connectedCallback=function(){this.hasAttribute("sort")?jb(this):this.setAttribute("sort","alpha");(this.hasAttribute("filter")||this.hasAttribute("category")||this.hasAttribute("tags"))&&kb(this)};P.prototype.connectedCallback=P.prototype.connectedCallback;P.prototype.attributeChangedCallback=function(a){switch(a){case "sort":jb(this);break;case "filter":case "category":case "tags":kb(this)}}; +P.prototype.attributeChangedCallback=P.prototype.attributeChangedCallback; +function jb(a){var b=a.getAttribute("sort")||"alpha",c=[].concat(p(a.querySelectorAll(".card")));if(!(2>c.length)){switch(b){case "duration":c.sort(a.i.bind(a));break;case "recent":c.sort(a.c.bind(a));break;default:b="alpha",c.sort(a.b.bind(a))}c.forEach(function(b){return a.appendChild(b)});c=new URL(document.location.toString());b&&"alpha"!==b?c.searchParams.set("sort",b):c.searchParams.delete("sort");lb(a);b=c.pathname+c.search;window.history.replaceState({path:b},document.title,b)}} +function lb(a){a.setAttribute("num",a.querySelectorAll(".card:not([hidden])").length)}P.prototype.i=function(a,b){if(!a||!b)return 0;var c=parseFloat(a.getAttribute("duration"))||0,d=parseFloat(b.getAttribute("duration"))||0;c-=d;return 0===c?this.c(a,b):c};P.prototype.c=function(a,b){if(!a||!b)return 0;var c=new Date(a.getAttribute("updated")||0);c=(new Date(b.getAttribute("updated")||0)).getTime()-c.getTime();return 0===c?this.b(a,b):c}; +P.prototype.b=function(a,b){if(!a||!b)return 0;a=a.getAttribute("title");b=b.getAttribute("title");return ab?1:0}; +function kb(a){var b=mb(a.getAttribute("filter")),c=Q((a.getAttribute("tags")||"").split(",")),d=Q((a.getAttribute("category")||"").split(","));[].concat(p(a.querySelectorAll(".card"))).forEach(function(a){var e=mb(a.getAttribute("title")),f=Q((a.getAttribute("category")||"").split(",")),Sb=Q((a.getAttribute("tags")||"").split(",")),Pa=!0,Qa=!0,Ra=!0;b&&(Pa=-1!==e.indexOf(b));c.length&&(Qa=nb(Sb,c));d.length&&(Ra=nb(f,d));Pa&&Qa&&Ra?a.removeAttribute("hidden"):a.setAttribute("hidden","")});var e= +new URL(document.location.toString());c.length?e.searchParams.set("tags",c.join(",")):e.searchParams.delete("tags");d.length?e.searchParams.set("cat",d.join(",")):e.searchParams.delete("cat");b?e.searchParams.set("filter",b):e.searchParams.delete("filter");lb(a);a=e.pathname+e.search;window.history.replaceState({path:a},document.title,a)}function nb(a,b){for(var c=0;c'+(d?''+(1==d?M(d)+" min remaining":M(d)+" mins remaining"):"")+'
'+M(c)+'
");eb(a,ib(b));a.classList.add("card");b=new URL(a.href,document.location.origin); +b.searchParams.has("index")||b.searchParams.set("index",document.location.pathname);b=b.href;b instanceof B||b instanceof B||(b="object"==typeof b&&b.i?b.c():String(b),Fa.test(b)||(b="about:invalid#zClosurez"),b=Ga(b));a.href=b instanceof B&&b.constructor===B&&b.v===Ea?b.a:"type_error:SafeUrl";if(b=a.getAttribute("id"))b=this.j.get("progress_"+b),c=a.getAttribute("steps"),b&&c&&a.setAttribute("progress",(parseFloat(b)/parseFloat(c)-1).toFixed(2));this.appendChild(a)};P.prototype.addCard=P.prototype.G; +h.Object.defineProperties(P,{observedAttributes:{configurable:!0,enumerable:!0,get:function(){return["sort","filter","category","tags"]}}});try{window.customElements.define("google-codelab-index-cards",P)}catch(a){console.warn("googlecodelabs.CodelabIndex.Cards",a)};function R(){0!=ob&&(this[la]||(this[la]=++ma))}var ob=0;var pb;(pb=!I)||(pb=9<=Number(Va));var qb=pb,rb; +if(rb=I){var sb;if(Object.prototype.hasOwnProperty.call(Ua,"9"))sb=Ua["9"];else{for(var S=0,tb=va(String(J)).split("."),ub=va("9").split("."),vb=Math.max(tb.length,ub.length),T=0;0==S&&T=a.keyCode)a.keyCode=-1}catch(b){}};var Cb="closure_listenable_"+(1E6*Math.random()|0),Db=0;function Eb(a,b,c,d,e){this.listener=a;this.proxy=null;this.src=b;this.type=c;this.capture=!!d;this.a=e;this.key=++Db;this.o=this.A=!1}function Fb(a){a.o=!0;a.listener=null;a.proxy=null;a.src=null;a.a=null};function Gb(a){this.src=a;this.a={};this.b=0}Gb.prototype.add=function(a,b,c,d,e){var f=a.toString();a=this.a[f];a||(a=this.a[f]=[],this.b++);var g;a:{for(g=0;gd.keyCode||void 0!=d.returnValue)){a:{var e=!1;if(0==d.keyCode)try{d.keyCode=-1;break a}catch(g){e=!0}if(e||void 0==d.returnValue)d.returnValue=!0}d=[];for(e=b.a;e;e=e.parentNode)d.push(e);a=a.type;for(e=d.length-1;0<=e;e--){b.a=d[e];var f=Vb(d[e],a,!0,b);c=c&&f}for(e=0;e>>0);function Nb(a){if("function"==u(a))return a;a[Xb]||(a[Xb]=function(b){return a.handleEvent(b)});return a[Xb]};function Yb(a,b){if("function"!=u(a))if(a&&"function"==typeof a.handleEvent)a=w(a.handleEvent,a);else throw Error("Invalid listener argument");return 2147483647=} visitedImports\n */\nexport function walkDeepDescendantElements(root, callback, visitedImports = new Set()) {\n let node = root;\n while (node) {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const element = /** @type {!Element} */(node);\n\n callback(element);\n\n const localName = element.localName;\n if (localName === 'link' && element.getAttribute('rel') === 'import') {\n // If this import (polyfilled or not) has it's root node available,\n // walk it.\n const importNode = /** @type {!Node} */ (element.import);\n if (importNode instanceof Node && !visitedImports.has(importNode)) {\n // Prevent multiple walks of the same import root.\n visitedImports.add(importNode);\n\n for (let child = importNode.firstChild; child; child = child.nextSibling) {\n walkDeepDescendantElements(child, callback, visitedImports);\n }\n }\n\n // Ignore descendants of import links to prevent attempting to walk the\n // elements created by the HTML Imports polyfill that we just walked\n // above.\n node = nextSiblingOrAncestorSibling(root, element);\n continue;\n } else if (localName === 'template') {\n // Ignore descendants of templates. There shouldn't be any descendants\n // because they will be moved into `.content` during construction in\n // browsers that support template but, in case they exist and are still\n // waiting to be moved by a polyfill, they will be ignored.\n node = nextSiblingOrAncestorSibling(root, element);\n continue;\n }\n\n // Walk shadow roots.\n const shadowRoot = element.__CE_shadowRoot;\n if (shadowRoot) {\n for (let child = shadowRoot.firstChild; child; child = child.nextSibling) {\n walkDeepDescendantElements(child, callback, visitedImports);\n }\n }\n }\n\n node = nextNode(root, node);\n }\n}\n\n/**\n * Used to suppress Closure's \"Modifying the prototype is only allowed if the\n * constructor is in the same scope\" warning without using\n * `@suppress {newCheckTypes, duplicate}` because `newCheckTypes` is too broad.\n *\n * @param {!Object} destination\n * @param {string} name\n * @param {*} value\n */\nexport function setPropertyUnchecked(destination, name, value) {\n destination[name] = value;\n}\n","import * as Utilities from './Utilities.js';\nimport CEState from './CustomElementState.js';\n\nexport default class CustomElementInternals {\n constructor() {\n /** @type {!Map} */\n this._localNameToDefinition = new Map();\n\n /** @type {!Map} */\n this._constructorToDefinition = new Map();\n\n /** @type {!Array} */\n this._patches = [];\n\n /** @type {boolean} */\n this._hasPatches = false;\n }\n\n /**\n * @param {string} localName\n * @param {!CustomElementDefinition} definition\n */\n setDefinition(localName, definition) {\n this._localNameToDefinition.set(localName, definition);\n this._constructorToDefinition.set(definition.constructor, definition);\n }\n\n /**\n * @param {string} localName\n * @return {!CustomElementDefinition|undefined}\n */\n localNameToDefinition(localName) {\n return this._localNameToDefinition.get(localName);\n }\n\n /**\n * @param {!Function} constructor\n * @return {!CustomElementDefinition|undefined}\n */\n constructorToDefinition(constructor) {\n return this._constructorToDefinition.get(constructor);\n }\n\n /**\n * @param {!function(!Node)} listener\n */\n addPatch(listener) {\n this._hasPatches = true;\n this._patches.push(listener);\n }\n\n /**\n * @param {!Node} node\n */\n patchTree(node) {\n if (!this._hasPatches) return;\n\n Utilities.walkDeepDescendantElements(node, element => this.patch(element));\n }\n\n /**\n * @param {!Node} node\n */\n patch(node) {\n if (!this._hasPatches) return;\n\n if (node.__CE_patched) return;\n node.__CE_patched = true;\n\n for (let i = 0; i < this._patches.length; i++) {\n this._patches[i](node);\n }\n }\n\n /**\n * @param {!Node} root\n */\n connectTree(root) {\n const elements = [];\n\n Utilities.walkDeepDescendantElements(root, element => elements.push(element));\n\n for (let i = 0; i < elements.length; i++) {\n const element = elements[i];\n if (element.__CE_state === CEState.custom) {\n this.connectedCallback(element);\n } else {\n this.upgradeElement(element);\n }\n }\n }\n\n /**\n * @param {!Node} root\n */\n disconnectTree(root) {\n const elements = [];\n\n Utilities.walkDeepDescendantElements(root, element => elements.push(element));\n\n for (let i = 0; i < elements.length; i++) {\n const element = elements[i];\n if (element.__CE_state === CEState.custom) {\n this.disconnectedCallback(element);\n }\n }\n }\n\n /**\n * Upgrades all uncustomized custom elements at and below a root node for\n * which there is a definition. When custom element reaction callbacks are\n * assumed to be called synchronously (which, by the current DOM / HTML spec\n * definitions, they are *not*), callbacks for both elements customized\n * synchronously by the parser and elements being upgraded occur in the same\n * relative order.\n *\n * NOTE: This function, when used to simulate the construction of a tree that\n * is already created but not customized (i.e. by the parser), does *not*\n * prevent the element from reading the 'final' (true) state of the tree. For\n * example, the element, during truly synchronous parsing / construction would\n * see that it contains no children as they have not yet been inserted.\n * However, this function does not modify the tree, the element will\n * (incorrectly) have children. Additionally, self-modification restrictions\n * for custom element constructors imposed by the DOM spec are *not* enforced.\n *\n *\n * The following nested list shows the steps extending down from the HTML\n * spec's parsing section that cause elements to be synchronously created and\n * upgraded:\n *\n * The \"in body\" insertion mode:\n * https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inbody\n * - Switch on token:\n * .. other cases ..\n * -> Any other start tag\n * - [Insert an HTML element](below) for the token.\n *\n * Insert an HTML element:\n * https://html.spec.whatwg.org/multipage/syntax.html#insert-an-html-element\n * - Insert a foreign element for the token in the HTML namespace:\n * https://html.spec.whatwg.org/multipage/syntax.html#insert-a-foreign-element\n * - Create an element for a token:\n * https://html.spec.whatwg.org/multipage/syntax.html#create-an-element-for-the-token\n * - Will execute script flag is true?\n * - (Element queue pushed to the custom element reactions stack.)\n * - Create an element:\n * https://dom.spec.whatwg.org/#concept-create-element\n * - Sync CE flag is true?\n * - Constructor called.\n * - Self-modification restrictions enforced.\n * - Sync CE flag is false?\n * - (Upgrade reaction enqueued.)\n * - Attributes appended to element.\n * (`attributeChangedCallback` reactions enqueued.)\n * - Will execute script flag is true?\n * - (Element queue popped from the custom element reactions stack.\n * Reactions in the popped stack are invoked.)\n * - (Element queue pushed to the custom element reactions stack.)\n * - Insert the element:\n * https://dom.spec.whatwg.org/#concept-node-insert\n * - Shadow-including descendants are connected. During parsing\n * construction, there are no shadow-*excluding* descendants.\n * However, the constructor may have validly attached a shadow\n * tree to itself and added descendants to that shadow tree.\n * (`connectedCallback` reactions enqueued.)\n * - (Element queue popped from the custom element reactions stack.\n * Reactions in the popped stack are invoked.)\n *\n * @param {!Node} root\n * @param {{\n * visitedImports: (!Set|undefined),\n * upgrade: (!function(!Element)|undefined),\n * }=} options\n */\n patchAndUpgradeTree(root, options = {}) {\n const visitedImports = options.visitedImports || new Set();\n const upgrade = options.upgrade || (element => this.upgradeElement(element));\n\n const elements = [];\n\n const gatherElements = element => {\n if (element.localName === 'link' && element.getAttribute('rel') === 'import') {\n // The HTML Imports polyfill sets a descendant element of the link to\n // the `import` property, specifically this is *not* a Document.\n const importNode = /** @type {?Node} */ (element.import);\n\n if (importNode instanceof Node) {\n importNode.__CE_isImportDocument = true;\n // Connected links are associated with the registry.\n importNode.__CE_hasRegistry = true;\n }\n\n if (importNode && importNode.readyState === 'complete') {\n importNode.__CE_documentLoadHandled = true;\n } else {\n // If this link's import root is not available, its contents can't be\n // walked. Wait for 'load' and walk it when it's ready.\n element.addEventListener('load', () => {\n const importNode = /** @type {!Node} */ (element.import);\n\n if (importNode.__CE_documentLoadHandled) return;\n importNode.__CE_documentLoadHandled = true;\n\n // Clone the `visitedImports` set that was populated sync during\n // the `patchAndUpgradeTree` call that caused this 'load' handler to\n // be added. Then, remove *this* link's import node so that we can\n // walk that import again, even if it was partially walked later\n // during the same `patchAndUpgradeTree` call.\n const clonedVisitedImports = new Set(visitedImports);\n clonedVisitedImports.delete(importNode);\n\n this.patchAndUpgradeTree(importNode, {visitedImports: clonedVisitedImports, upgrade});\n });\n }\n } else {\n elements.push(element);\n }\n };\n\n // `walkDeepDescendantElements` populates (and internally checks against)\n // `visitedImports` when traversing a loaded import.\n Utilities.walkDeepDescendantElements(root, gatherElements, visitedImports);\n\n if (this._hasPatches) {\n for (let i = 0; i < elements.length; i++) {\n this.patch(elements[i]);\n }\n }\n\n for (let i = 0; i < elements.length; i++) {\n upgrade(elements[i]);\n }\n }\n\n /**\n * @param {!Element} element\n */\n upgradeElement(element) {\n const currentState = element.__CE_state;\n if (currentState !== undefined) return;\n\n // Prevent elements created in documents without a browsing context from\n // upgrading.\n //\n // https://html.spec.whatwg.org/multipage/custom-elements.html#look-up-a-custom-element-definition\n // \"If document does not have a browsing context, return null.\"\n //\n // https://html.spec.whatwg.org/multipage/window-object.html#dom-document-defaultview\n // \"The defaultView IDL attribute of the Document interface, on getting,\n // must return this Document's browsing context's WindowProxy object, if\n // this Document has an associated browsing context, or null otherwise.\"\n const ownerDocument = element.ownerDocument;\n if (\n !ownerDocument.defaultView &&\n !(ownerDocument.__CE_isImportDocument && ownerDocument.__CE_hasRegistry)\n ) return;\n\n const definition = this.localNameToDefinition(element.localName);\n if (!definition) return;\n\n definition.constructionStack.push(element);\n\n const constructor = definition.constructor;\n try {\n try {\n let result = new (constructor)();\n if (result !== element) {\n throw new Error('The custom element constructor did not produce the element being upgraded.');\n }\n } finally {\n definition.constructionStack.pop();\n }\n } catch (e) {\n element.__CE_state = CEState.failed;\n throw e;\n }\n\n element.__CE_state = CEState.custom;\n element.__CE_definition = definition;\n\n if (definition.attributeChangedCallback) {\n const observedAttributes = definition.observedAttributes;\n for (let i = 0; i < observedAttributes.length; i++) {\n const name = observedAttributes[i];\n const value = element.getAttribute(name);\n if (value !== null) {\n this.attributeChangedCallback(element, name, null, value, null);\n }\n }\n }\n\n if (Utilities.isConnected(element)) {\n this.connectedCallback(element);\n }\n }\n\n /**\n * @param {!Element} element\n */\n connectedCallback(element) {\n const definition = element.__CE_definition;\n if (definition.connectedCallback) {\n definition.connectedCallback.call(element);\n }\n }\n\n /**\n * @param {!Element} element\n */\n disconnectedCallback(element) {\n const definition = element.__CE_definition;\n if (definition.disconnectedCallback) {\n definition.disconnectedCallback.call(element);\n }\n }\n\n /**\n * @param {!Element} element\n * @param {string} name\n * @param {?string} oldValue\n * @param {?string} newValue\n * @param {?string} namespace\n */\n attributeChangedCallback(element, name, oldValue, newValue, namespace) {\n const definition = element.__CE_definition;\n if (\n definition.attributeChangedCallback &&\n definition.observedAttributes.indexOf(name) > -1\n ) {\n definition.attributeChangedCallback.call(element, name, oldValue, newValue, namespace);\n }\n }\n}\n","/**\n * @enum {number}\n */\nconst CustomElementState = {\n custom: 1,\n failed: 2,\n};\n\nexport default CustomElementState;\n","import CustomElementInternals from './CustomElementInternals.js';\n\nexport default class DocumentConstructionObserver {\n constructor(internals, doc) {\n /**\n * @type {!CustomElementInternals}\n */\n this._internals = internals;\n\n /**\n * @type {!Document}\n */\n this._document = doc;\n\n /**\n * @type {MutationObserver|undefined}\n */\n this._observer = undefined;\n\n\n // Simulate tree construction for all currently accessible nodes in the\n // document.\n this._internals.patchAndUpgradeTree(this._document);\n\n if (this._document.readyState === 'loading') {\n this._observer = new MutationObserver(this._handleMutations.bind(this));\n\n // Nodes created by the parser are given to the observer *before* the next\n // task runs. Inline scripts are run in a new task. This means that the\n // observer will be able to handle the newly parsed nodes before the inline\n // script is run.\n this._observer.observe(this._document, {\n childList: true,\n subtree: true,\n });\n }\n }\n\n disconnect() {\n if (this._observer) {\n this._observer.disconnect();\n }\n }\n\n /**\n * @param {!Array} mutations\n */\n _handleMutations(mutations) {\n // Once the document's `readyState` is 'interactive' or 'complete', all new\n // nodes created within that document will be the result of script and\n // should be handled by patching.\n const readyState = this._document.readyState;\n if (readyState === 'interactive' || readyState === 'complete') {\n this.disconnect();\n }\n\n for (let i = 0; i < mutations.length; i++) {\n const addedNodes = mutations[i].addedNodes;\n for (let j = 0; j < addedNodes.length; j++) {\n const node = addedNodes[j];\n this._internals.patchAndUpgradeTree(node);\n }\n }\n }\n}\n","/**\n * @template T\n */\nexport default class Deferred {\n constructor() {\n /**\n * @private\n * @type {T|undefined}\n */\n this._value = undefined;\n\n /**\n * @private\n * @type {Function|undefined}\n */\n this._resolve = undefined;\n\n /**\n * @private\n * @type {!Promise}\n */\n this._promise = new Promise(resolve => {\n this._resolve = resolve;\n\n if (this._value) {\n resolve(this._value);\n }\n });\n }\n\n /**\n * @param {T} value\n */\n resolve(value) {\n if (this._value) {\n throw new Error('Already resolved.');\n }\n\n this._value = value;\n\n if (this._resolve) {\n this._resolve(value);\n }\n }\n\n /**\n * @return {!Promise}\n */\n toPromise() {\n return this._promise;\n }\n}\n","import CustomElementInternals from './CustomElementInternals.js';\nimport DocumentConstructionObserver from './DocumentConstructionObserver.js';\nimport Deferred from './Deferred.js';\nimport * as Utilities from './Utilities.js';\n\n/**\n * @unrestricted\n */\nexport default class CustomElementRegistry {\n\n /**\n * @param {!CustomElementInternals} internals\n */\n constructor(internals) {\n /**\n * @private\n * @type {boolean}\n */\n this._elementDefinitionIsRunning = false;\n\n /**\n * @private\n * @type {!CustomElementInternals}\n */\n this._internals = internals;\n\n /**\n * @private\n * @type {!Map>}\n */\n this._whenDefinedDeferred = new Map();\n\n /**\n * The default flush callback triggers the document walk synchronously.\n * @private\n * @type {!Function}\n */\n this._flushCallback = fn => fn();\n\n /**\n * @private\n * @type {boolean}\n */\n this._flushPending = false;\n\n /**\n * @private\n * @type {!Array}\n */\n this._pendingDefinitions = [];\n\n /**\n * @private\n * @type {!DocumentConstructionObserver}\n */\n this._documentConstructionObserver = new DocumentConstructionObserver(internals, document);\n }\n\n /**\n * @param {string} localName\n * @param {!Function} constructor\n */\n define(localName, constructor) {\n if (!(constructor instanceof Function)) {\n throw new TypeError('Custom element constructors must be functions.');\n }\n\n if (!Utilities.isValidCustomElementName(localName)) {\n throw new SyntaxError(`The element name '${localName}' is not valid.`);\n }\n\n if (this._internals.localNameToDefinition(localName)) {\n throw new Error(`A custom element with name '${localName}' has already been defined.`);\n }\n\n if (this._elementDefinitionIsRunning) {\n throw new Error('A custom element is already being defined.');\n }\n this._elementDefinitionIsRunning = true;\n\n let connectedCallback;\n let disconnectedCallback;\n let adoptedCallback;\n let attributeChangedCallback;\n let observedAttributes;\n try {\n /** @type {!Object} */\n const prototype = constructor.prototype;\n if (!(prototype instanceof Object)) {\n throw new TypeError('The custom element constructor\\'s prototype is not an object.');\n }\n\n function getCallback(name) {\n const callbackValue = prototype[name];\n if (callbackValue !== undefined && !(callbackValue instanceof Function)) {\n throw new Error(`The '${name}' callback must be a function.`);\n }\n return callbackValue;\n }\n\n connectedCallback = getCallback('connectedCallback');\n disconnectedCallback = getCallback('disconnectedCallback');\n adoptedCallback = getCallback('adoptedCallback');\n attributeChangedCallback = getCallback('attributeChangedCallback');\n observedAttributes = constructor['observedAttributes'] || [];\n } catch (e) {\n return;\n } finally {\n this._elementDefinitionIsRunning = false;\n }\n\n const definition = {\n localName,\n constructor,\n connectedCallback,\n disconnectedCallback,\n adoptedCallback,\n attributeChangedCallback,\n observedAttributes,\n constructionStack: [],\n };\n\n this._internals.setDefinition(localName, definition);\n this._pendingDefinitions.push(definition);\n\n // If we've already called the flush callback and it hasn't called back yet,\n // don't call it again.\n if (!this._flushPending) {\n this._flushPending = true;\n this._flushCallback(() => this._flush());\n }\n }\n\n _flush() {\n // If no new definitions were defined, don't attempt to flush. This could\n // happen if a flush callback keeps the function it is given and calls it\n // multiple times.\n if (this._flushPending === false) return;\n this._flushPending = false;\n\n const pendingDefinitions = this._pendingDefinitions;\n\n /**\n * Unupgraded elements with definitions that were defined *before* the last\n * flush, in document order.\n * @type {!Array}\n */\n const elementsWithStableDefinitions = [];\n\n /**\n * A map from `localName`s of definitions that were defined *after* the last\n * flush to unupgraded elements matching that definition, in document order.\n * @type {!Map>}\n */\n const elementsWithPendingDefinitions = new Map();\n for (let i = 0; i < pendingDefinitions.length; i++) {\n elementsWithPendingDefinitions.set(pendingDefinitions[i].localName, []);\n }\n\n this._internals.patchAndUpgradeTree(document, {\n upgrade: element => {\n // Ignore the element if it has already upgraded or failed to upgrade.\n if (element.__CE_state !== undefined) return;\n\n const localName = element.localName;\n\n // If there is an applicable pending definition for the element, add the\n // element to the list of elements to be upgraded with that definition.\n const pendingElements = elementsWithPendingDefinitions.get(localName);\n if (pendingElements) {\n pendingElements.push(element);\n // If there is *any other* applicable definition for the element, add it\n // to the list of elements with stable definitions that need to be upgraded.\n } else if (this._internals.localNameToDefinition(localName)) {\n elementsWithStableDefinitions.push(element);\n }\n },\n });\n\n // Upgrade elements with 'stable' definitions first.\n for (let i = 0; i < elementsWithStableDefinitions.length; i++) {\n this._internals.upgradeElement(elementsWithStableDefinitions[i]);\n }\n\n // Upgrade elements with 'pending' definitions in the order they were defined.\n while (pendingDefinitions.length > 0) {\n const definition = pendingDefinitions.shift();\n const localName = definition.localName;\n\n // Attempt to upgrade all applicable elements.\n const pendingUpgradableElements = elementsWithPendingDefinitions.get(definition.localName);\n for (let i = 0; i < pendingUpgradableElements.length; i++) {\n this._internals.upgradeElement(pendingUpgradableElements[i]);\n }\n\n // Resolve any promises created by `whenDefined` for the definition.\n const deferred = this._whenDefinedDeferred.get(localName);\n if (deferred) {\n deferred.resolve(undefined);\n }\n }\n }\n\n /**\n * @param {string} localName\n * @return {Function|undefined}\n */\n get(localName) {\n const definition = this._internals.localNameToDefinition(localName);\n if (definition) {\n return definition.constructor;\n }\n\n return undefined;\n }\n\n /**\n * @param {string} localName\n * @return {!Promise}\n */\n whenDefined(localName) {\n if (!Utilities.isValidCustomElementName(localName)) {\n return Promise.reject(new SyntaxError(`'${localName}' is not a valid custom element name.`));\n }\n\n const prior = this._whenDefinedDeferred.get(localName);\n if (prior) {\n return prior.toPromise();\n }\n\n const deferred = new Deferred();\n this._whenDefinedDeferred.set(localName, deferred);\n\n const definition = this._internals.localNameToDefinition(localName);\n // Resolve immediately only if the given local name has a definition *and*\n // the full document walk to upgrade elements with that local name has\n // already happened.\n if (definition && !this._pendingDefinitions.some(d => d.localName === localName)) {\n deferred.resolve(undefined);\n }\n\n return deferred.toPromise();\n }\n\n polyfillWrapFlushCallback(outer) {\n this._documentConstructionObserver.disconnect();\n const inner = this._flushCallback;\n this._flushCallback = flush => outer(() => inner(flush));\n }\n}\n\n// Closure compiler exports.\nwindow['CustomElementRegistry'] = CustomElementRegistry;\nCustomElementRegistry.prototype['define'] = CustomElementRegistry.prototype.define;\nCustomElementRegistry.prototype['get'] = CustomElementRegistry.prototype.get;\nCustomElementRegistry.prototype['whenDefined'] = CustomElementRegistry.prototype.whenDefined;\nCustomElementRegistry.prototype['polyfillWrapFlushCallback'] = CustomElementRegistry.prototype.polyfillWrapFlushCallback;\n","export default {\n Document_createElement: window.Document.prototype.createElement,\n Document_createElementNS: window.Document.prototype.createElementNS,\n Document_importNode: window.Document.prototype.importNode,\n Document_prepend: window.Document.prototype['prepend'],\n Document_append: window.Document.prototype['append'],\n DocumentFragment_prepend: window.DocumentFragment.prototype['prepend'],\n DocumentFragment_append: window.DocumentFragment.prototype['append'],\n Node_cloneNode: window.Node.prototype.cloneNode,\n Node_appendChild: window.Node.prototype.appendChild,\n Node_insertBefore: window.Node.prototype.insertBefore,\n Node_removeChild: window.Node.prototype.removeChild,\n Node_replaceChild: window.Node.prototype.replaceChild,\n Node_textContent: Object.getOwnPropertyDescriptor(window.Node.prototype, 'textContent'),\n Element_attachShadow: window.Element.prototype['attachShadow'],\n Element_innerHTML: Object.getOwnPropertyDescriptor(window.Element.prototype, 'innerHTML'),\n Element_getAttribute: window.Element.prototype.getAttribute,\n Element_setAttribute: window.Element.prototype.setAttribute,\n Element_removeAttribute: window.Element.prototype.removeAttribute,\n Element_getAttributeNS: window.Element.prototype.getAttributeNS,\n Element_setAttributeNS: window.Element.prototype.setAttributeNS,\n Element_removeAttributeNS: window.Element.prototype.removeAttributeNS,\n Element_insertAdjacentElement: window.Element.prototype['insertAdjacentElement'],\n Element_prepend: window.Element.prototype['prepend'],\n Element_append: window.Element.prototype['append'],\n Element_before: window.Element.prototype['before'],\n Element_after: window.Element.prototype['after'],\n Element_replaceWith: window.Element.prototype['replaceWith'],\n Element_remove: window.Element.prototype['remove'],\n HTMLElement: window.HTMLElement,\n HTMLElement_innerHTML: Object.getOwnPropertyDescriptor(window.HTMLElement.prototype, 'innerHTML'),\n HTMLElement_insertAdjacentElement: window.HTMLElement.prototype['insertAdjacentElement'],\n};\n","import Native from './Native.js';\nimport CustomElementInternals from '../CustomElementInternals.js';\nimport CEState from '../CustomElementState.js';\nimport AlreadyConstructedMarker from '../AlreadyConstructedMarker.js';\n\n/**\n * @param {!CustomElementInternals} internals\n */\nexport default function(internals) {\n window['HTMLElement'] = (function() {\n /**\n * @type {function(new: HTMLElement): !HTMLElement}\n */\n function HTMLElement() {\n // This should really be `new.target` but `new.target` can't be emulated\n // in ES5. Assuming the user keeps the default value of the constructor's\n // prototype's `constructor` property, this is equivalent.\n /** @type {!Function} */\n const constructor = this.constructor;\n\n const definition = internals.constructorToDefinition(constructor);\n if (!definition) {\n throw new Error('The custom element being constructed was not registered with `customElements`.');\n }\n\n const constructionStack = definition.constructionStack;\n\n if (constructionStack.length === 0) {\n const element = Native.Document_createElement.call(document, definition.localName);\n Object.setPrototypeOf(element, constructor.prototype);\n element.__CE_state = CEState.custom;\n element.__CE_definition = definition;\n internals.patch(element);\n return element;\n }\n\n const lastIndex = constructionStack.length - 1;\n const element = constructionStack[lastIndex];\n if (element === AlreadyConstructedMarker) {\n throw new Error('The HTMLElement constructor was either called reentrantly for this constructor or called multiple times.');\n }\n constructionStack[lastIndex] = AlreadyConstructedMarker;\n\n Object.setPrototypeOf(element, constructor.prototype);\n internals.patch(/** @type {!HTMLElement} */ (element));\n\n return element;\n }\n\n HTMLElement.prototype = Native.HTMLElement.prototype;\n\n return HTMLElement;\n })();\n};\n","/**\n * @license\n * Copyright (c) 2016 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n */\n\nimport CustomElementInternals from './CustomElementInternals.js';\nimport CustomElementRegistry from './CustomElementRegistry.js';\n\nimport PatchHTMLElement from './Patch/HTMLElement.js';\nimport PatchDocument from './Patch/Document.js';\nimport PatchDocumentFragment from './Patch/DocumentFragment.js';\nimport PatchNode from './Patch/Node.js';\nimport PatchElement from './Patch/Element.js';\n\nconst priorCustomElements = window['customElements'];\n\nif (!priorCustomElements ||\n priorCustomElements['forcePolyfill'] ||\n (typeof priorCustomElements['define'] != 'function') ||\n (typeof priorCustomElements['get'] != 'function')) {\n /** @type {!CustomElementInternals} */\n const internals = new CustomElementInternals();\n\n PatchHTMLElement(internals);\n PatchDocument(internals);\n PatchDocumentFragment(internals);\n PatchNode(internals);\n PatchElement(internals);\n\n // The main document is always associated with the registry.\n document.__CE_hasRegistry = true;\n\n /** @type {!CustomElementRegistry} */\n const customElements = new CustomElementRegistry(internals);\n\n Object.defineProperty(window, 'customElements', {\n configurable: true,\n enumerable: true,\n value: customElements,\n });\n}\n","import CustomElementInternals from '../../CustomElementInternals.js';\nimport * as Utilities from '../../Utilities.js';\n\n/**\n * @typedef {{\n * prepend: !function(...(!Node|string)),\n * append: !function(...(!Node|string)),\n * }}\n */\nlet ParentNodeNativeMethods;\n\n/**\n * @param {!CustomElementInternals} internals\n * @param {!Object} destination\n * @param {!ParentNodeNativeMethods} builtIn\n */\nexport default function(internals, destination, builtIn) {\n /**\n * @param {!function(...(!Node|string))} builtInMethod\n * @return {!function(...(!Node|string))}\n */\n function appendPrependPatch(builtInMethod) {\n return function(...nodes) {\n /**\n * A copy of `nodes`, with any DocumentFragment replaced by its children.\n * @type {!Array}\n */\n const flattenedNodes = [];\n\n /**\n * Elements in `nodes` that were connected before this call.\n * @type {!Array}\n */\n const connectedElements = [];\n\n for (var i = 0; i < nodes.length; i++) {\n const node = nodes[i];\n\n if (node instanceof Element && Utilities.isConnected(node)) {\n connectedElements.push(node);\n }\n\n if (node instanceof DocumentFragment) {\n for (let child = node.firstChild; child; child = child.nextSibling) {\n flattenedNodes.push(child);\n }\n } else {\n flattenedNodes.push(node);\n }\n }\n\n builtInMethod.apply(this, nodes);\n\n for (let i = 0; i < connectedElements.length; i++) {\n internals.disconnectTree(connectedElements[i]);\n }\n\n if (Utilities.isConnected(this)) {\n for (let i = 0; i < flattenedNodes.length; i++) {\n const node = flattenedNodes[i];\n if (node instanceof Element) {\n internals.connectTree(node);\n }\n }\n }\n };\n }\n\n if (builtIn.prepend !== undefined) {\n Utilities.setPropertyUnchecked(destination, 'prepend', appendPrependPatch(builtIn.prepend));\n }\n\n if (builtIn.append !== undefined) {\n Utilities.setPropertyUnchecked(destination, 'append', appendPrependPatch(builtIn.append));\n }\n};\n","import Native from './Native.js';\nimport CustomElementInternals from '../CustomElementInternals.js';\nimport * as Utilities from '../Utilities.js';\n\nimport PatchParentNode from './Interface/ParentNode.js';\n\n/**\n * @param {!CustomElementInternals} internals\n */\nexport default function(internals) {\n Utilities.setPropertyUnchecked(Document.prototype, 'createElement',\n /**\n * @this {Document}\n * @param {string} localName\n * @return {!Element}\n */\n function(localName) {\n // Only create custom elements if this document is associated with the registry.\n if (this.__CE_hasRegistry) {\n const definition = internals.localNameToDefinition(localName);\n if (definition) {\n return new (definition.constructor)();\n }\n }\n\n const result = /** @type {!Element} */\n (Native.Document_createElement.call(this, localName));\n internals.patch(result);\n return result;\n });\n\n Utilities.setPropertyUnchecked(Document.prototype, 'importNode',\n /**\n * @this {Document}\n * @param {!Node} node\n * @param {boolean=} deep\n * @return {!Node}\n */\n function(node, deep) {\n const clone = Native.Document_importNode.call(this, node, deep);\n // Only create custom elements if this document is associated with the registry.\n if (!this.__CE_hasRegistry) {\n internals.patchTree(clone);\n } else {\n internals.patchAndUpgradeTree(clone);\n }\n return clone;\n });\n\n const NS_HTML = \"http://www.w3.org/1999/xhtml\";\n\n Utilities.setPropertyUnchecked(Document.prototype, 'createElementNS',\n /**\n * @this {Document}\n * @param {?string} namespace\n * @param {string} localName\n * @return {!Element}\n */\n function(namespace, localName) {\n // Only create custom elements if this document is associated with the registry.\n if (this.__CE_hasRegistry && (namespace === null || namespace === NS_HTML)) {\n const definition = internals.localNameToDefinition(localName);\n if (definition) {\n return new (definition.constructor)();\n }\n }\n\n const result = /** @type {!Element} */\n (Native.Document_createElementNS.call(this, namespace, localName));\n internals.patch(result);\n return result;\n });\n\n PatchParentNode(internals, Document.prototype, {\n prepend: Native.Document_prepend,\n append: Native.Document_append,\n });\n};\n","import Native from './Native.js';\nimport CustomElementInternals from '../CustomElementInternals.js';\nimport * as Utilities from '../Utilities.js';\n\n/**\n * @param {!CustomElementInternals} internals\n */\nexport default function(internals) {\n // `Node#nodeValue` is implemented on `Attr`.\n // `Node#textContent` is implemented on `Attr`, `Element`.\n\n Utilities.setPropertyUnchecked(Node.prototype, 'insertBefore',\n /**\n * @this {Node}\n * @param {!Node} node\n * @param {?Node} refNode\n * @return {!Node}\n */\n function(node, refNode) {\n if (node instanceof DocumentFragment) {\n const insertedNodes = Array.prototype.slice.apply(node.childNodes);\n const nativeResult = Native.Node_insertBefore.call(this, node, refNode);\n\n // DocumentFragments can't be connected, so `disconnectTree` will never\n // need to be called on a DocumentFragment's children after inserting it.\n\n if (Utilities.isConnected(this)) {\n for (let i = 0; i < insertedNodes.length; i++) {\n internals.connectTree(insertedNodes[i]);\n }\n }\n\n return nativeResult;\n }\n\n const nodeWasConnected = Utilities.isConnected(node);\n const nativeResult = Native.Node_insertBefore.call(this, node, refNode);\n\n if (nodeWasConnected) {\n internals.disconnectTree(node);\n }\n\n if (Utilities.isConnected(this)) {\n internals.connectTree(node);\n }\n\n return nativeResult;\n });\n\n Utilities.setPropertyUnchecked(Node.prototype, 'appendChild',\n /**\n * @this {Node}\n * @param {!Node} node\n * @return {!Node}\n */\n function(node) {\n if (node instanceof DocumentFragment) {\n const insertedNodes = Array.prototype.slice.apply(node.childNodes);\n const nativeResult = Native.Node_appendChild.call(this, node);\n\n // DocumentFragments can't be connected, so `disconnectTree` will never\n // need to be called on a DocumentFragment's children after inserting it.\n\n if (Utilities.isConnected(this)) {\n for (let i = 0; i < insertedNodes.length; i++) {\n internals.connectTree(insertedNodes[i]);\n }\n }\n\n return nativeResult;\n }\n\n const nodeWasConnected = Utilities.isConnected(node);\n const nativeResult = Native.Node_appendChild.call(this, node);\n\n if (nodeWasConnected) {\n internals.disconnectTree(node);\n }\n\n if (Utilities.isConnected(this)) {\n internals.connectTree(node);\n }\n\n return nativeResult;\n });\n\n Utilities.setPropertyUnchecked(Node.prototype, 'cloneNode',\n /**\n * @this {Node}\n * @param {boolean=} deep\n * @return {!Node}\n */\n function(deep) {\n const clone = Native.Node_cloneNode.call(this, deep);\n // Only create custom elements if this element's owner document is\n // associated with the registry.\n if (!this.ownerDocument.__CE_hasRegistry) {\n internals.patchTree(clone);\n } else {\n internals.patchAndUpgradeTree(clone);\n }\n return clone;\n });\n\n Utilities.setPropertyUnchecked(Node.prototype, 'removeChild',\n /**\n * @this {Node}\n * @param {!Node} node\n * @return {!Node}\n */\n function(node) {\n const nodeWasConnected = Utilities.isConnected(node);\n const nativeResult = Native.Node_removeChild.call(this, node);\n\n if (nodeWasConnected) {\n internals.disconnectTree(node);\n }\n\n return nativeResult;\n });\n\n Utilities.setPropertyUnchecked(Node.prototype, 'replaceChild',\n /**\n * @this {Node}\n * @param {!Node} nodeToInsert\n * @param {!Node} nodeToRemove\n * @return {!Node}\n */\n function(nodeToInsert, nodeToRemove) {\n if (nodeToInsert instanceof DocumentFragment) {\n const insertedNodes = Array.prototype.slice.apply(nodeToInsert.childNodes);\n const nativeResult = Native.Node_replaceChild.call(this, nodeToInsert, nodeToRemove);\n\n // DocumentFragments can't be connected, so `disconnectTree` will never\n // need to be called on a DocumentFragment's children after inserting it.\n\n if (Utilities.isConnected(this)) {\n internals.disconnectTree(nodeToRemove);\n for (let i = 0; i < insertedNodes.length; i++) {\n internals.connectTree(insertedNodes[i]);\n }\n }\n\n return nativeResult;\n }\n\n const nodeToInsertWasConnected = Utilities.isConnected(nodeToInsert);\n const nativeResult = Native.Node_replaceChild.call(this, nodeToInsert, nodeToRemove);\n const thisIsConnected = Utilities.isConnected(this);\n\n if (thisIsConnected) {\n internals.disconnectTree(nodeToRemove);\n }\n\n if (nodeToInsertWasConnected) {\n internals.disconnectTree(nodeToInsert);\n }\n\n if (thisIsConnected) {\n internals.connectTree(nodeToInsert);\n }\n\n return nativeResult;\n });\n\n\n function patch_textContent(destination, baseDescriptor) {\n Object.defineProperty(destination, 'textContent', {\n enumerable: baseDescriptor.enumerable,\n configurable: true,\n get: baseDescriptor.get,\n set: /** @this {Node} */ function(assignedValue) {\n // If this is a text node then there are no nodes to disconnect.\n if (this.nodeType === Node.TEXT_NODE) {\n baseDescriptor.set.call(this, assignedValue);\n return;\n }\n\n let removedNodes = undefined;\n // Checking for `firstChild` is faster than reading `childNodes.length`\n // to compare with 0.\n if (this.firstChild) {\n // Using `childNodes` is faster than `children`, even though we only\n // care about elements.\n const childNodes = this.childNodes;\n const childNodesLength = childNodes.length;\n if (childNodesLength > 0 && Utilities.isConnected(this)) {\n // Copying an array by iterating is faster than using slice.\n removedNodes = new Array(childNodesLength);\n for (let i = 0; i < childNodesLength; i++) {\n removedNodes[i] = childNodes[i];\n }\n }\n }\n\n baseDescriptor.set.call(this, assignedValue);\n\n if (removedNodes) {\n for (let i = 0; i < removedNodes.length; i++) {\n internals.disconnectTree(removedNodes[i]);\n }\n }\n },\n });\n }\n\n if (Native.Node_textContent && Native.Node_textContent.get) {\n patch_textContent(Node.prototype, Native.Node_textContent);\n } else {\n internals.addPatch(function(element) {\n patch_textContent(element, {\n enumerable: true,\n configurable: true,\n // NOTE: This implementation of the `textContent` getter assumes that\n // text nodes' `textContent` getter will not be patched.\n get: /** @this {Node} */ function() {\n /** @type {!Array} */\n const parts = [];\n\n for (let i = 0; i < this.childNodes.length; i++) {\n parts.push(this.childNodes[i].textContent);\n }\n\n return parts.join('');\n },\n set: /** @this {Node} */ function(assignedValue) {\n while (this.firstChild) {\n Native.Node_removeChild.call(this, this.firstChild);\n }\n Native.Node_appendChild.call(this, document.createTextNode(assignedValue));\n },\n });\n });\n }\n};\n","import CustomElementInternals from '../../CustomElementInternals.js';\nimport * as Utilities from '../../Utilities.js';\n\n/**\n * @typedef {{\n * before: !function(...(!Node|string)),\n * after: !function(...(!Node|string)),\n * replaceWith: !function(...(!Node|string)),\n * remove: !function(),\n * }}\n */\nlet ChildNodeNativeMethods;\n\n/**\n * @param {!CustomElementInternals} internals\n * @param {!Object} destination\n * @param {!ChildNodeNativeMethods} builtIn\n */\nexport default function(internals, destination, builtIn) {\n /**\n * @param {!function(...(!Node|string))} builtInMethod\n * @return {!function(...(!Node|string))}\n */\n function beforeAfterPatch(builtInMethod) {\n return function(...nodes) {\n /**\n * A copy of `nodes`, with any DocumentFragment replaced by its children.\n * @type {!Array}\n */\n const flattenedNodes = [];\n\n /**\n * Elements in `nodes` that were connected before this call.\n * @type {!Array}\n */\n const connectedElements = [];\n\n for (var i = 0; i < nodes.length; i++) {\n const node = nodes[i];\n\n if (node instanceof Element && Utilities.isConnected(node)) {\n connectedElements.push(node);\n }\n\n if (node instanceof DocumentFragment) {\n for (let child = node.firstChild; child; child = child.nextSibling) {\n flattenedNodes.push(child);\n }\n } else {\n flattenedNodes.push(node);\n }\n }\n\n builtInMethod.apply(this, nodes);\n\n for (let i = 0; i < connectedElements.length; i++) {\n internals.disconnectTree(connectedElements[i]);\n }\n\n if (Utilities.isConnected(this)) {\n for (let i = 0; i < flattenedNodes.length; i++) {\n const node = flattenedNodes[i];\n if (node instanceof Element) {\n internals.connectTree(node);\n }\n }\n }\n };\n }\n\n if (builtIn.before !== undefined) {\n Utilities.setPropertyUnchecked(destination, 'before', beforeAfterPatch(builtIn.before));\n }\n\n if (builtIn.before !== undefined) {\n Utilities.setPropertyUnchecked(destination, 'after', beforeAfterPatch(builtIn.after));\n }\n\n if (builtIn.replaceWith !== undefined) {\n Utilities.setPropertyUnchecked(destination, 'replaceWith',\n /**\n * @param {...(!Node|string)} nodes\n */\n function(...nodes) {\n /**\n * A copy of `nodes`, with any DocumentFragment replaced by its children.\n * @type {!Array}\n */\n const flattenedNodes = [];\n\n /**\n * Elements in `nodes` that were connected before this call.\n * @type {!Array}\n */\n const connectedElements = [];\n\n for (var i = 0; i < nodes.length; i++) {\n const node = nodes[i];\n\n if (node instanceof Element && Utilities.isConnected(node)) {\n connectedElements.push(node);\n }\n\n if (node instanceof DocumentFragment) {\n for (let child = node.firstChild; child; child = child.nextSibling) {\n flattenedNodes.push(child);\n }\n } else {\n flattenedNodes.push(node);\n }\n }\n\n const wasConnected = Utilities.isConnected(this);\n\n builtIn.replaceWith.apply(this, nodes);\n\n for (let i = 0; i < connectedElements.length; i++) {\n internals.disconnectTree(connectedElements[i]);\n }\n\n if (wasConnected) {\n internals.disconnectTree(this);\n for (let i = 0; i < flattenedNodes.length; i++) {\n const node = flattenedNodes[i];\n if (node instanceof Element) {\n internals.connectTree(node);\n }\n }\n }\n });\n }\n\n if (builtIn.remove !== undefined) {\n Utilities.setPropertyUnchecked(destination, 'remove',\n function() {\n const wasConnected = Utilities.isConnected(this);\n\n builtIn.remove.call(this);\n\n if (wasConnected) {\n internals.disconnectTree(this);\n }\n });\n }\n};\n","import Native from './Native.js';\nimport CustomElementInternals from '../CustomElementInternals.js';\nimport CEState from '../CustomElementState.js';\nimport * as Utilities from '../Utilities.js';\n\nimport PatchParentNode from './Interface/ParentNode.js';\nimport PatchChildNode from './Interface/ChildNode.js';\n\n/**\n * @param {!CustomElementInternals} internals\n */\nexport default function(internals) {\n if (Native.Element_attachShadow) {\n Utilities.setPropertyUnchecked(Element.prototype, 'attachShadow',\n /**\n * @this {Element}\n * @param {!{mode: string}} init\n * @return {ShadowRoot}\n */\n function(init) {\n const shadowRoot = Native.Element_attachShadow.call(this, init);\n this.__CE_shadowRoot = shadowRoot;\n return shadowRoot;\n });\n }\n\n\n function patch_innerHTML(destination, baseDescriptor) {\n Object.defineProperty(destination, 'innerHTML', {\n enumerable: baseDescriptor.enumerable,\n configurable: true,\n get: baseDescriptor.get,\n set: /** @this {Element} */ function(htmlString) {\n const isConnected = Utilities.isConnected(this);\n\n // NOTE: In IE11, when using the native `innerHTML` setter, all nodes\n // that were previously descendants of the context element have all of\n // their children removed as part of the set - the entire subtree is\n // 'disassembled'. This work around walks the subtree *before* using the\n // native setter.\n /** @type {!Array|undefined} */\n let removedElements = undefined;\n if (isConnected) {\n removedElements = [];\n Utilities.walkDeepDescendantElements(this, element => {\n if (element !== this) {\n removedElements.push(element);\n }\n });\n }\n\n baseDescriptor.set.call(this, htmlString);\n\n if (removedElements) {\n for (let i = 0; i < removedElements.length; i++) {\n const element = removedElements[i];\n if (element.__CE_state === CEState.custom) {\n internals.disconnectedCallback(element);\n }\n }\n }\n\n // Only create custom elements if this element's owner document is\n // associated with the registry.\n if (!this.ownerDocument.__CE_hasRegistry) {\n internals.patchTree(this);\n } else {\n internals.patchAndUpgradeTree(this);\n }\n return htmlString;\n },\n });\n }\n\n if (Native.Element_innerHTML && Native.Element_innerHTML.get) {\n patch_innerHTML(Element.prototype, Native.Element_innerHTML);\n } else if (Native.HTMLElement_innerHTML && Native.HTMLElement_innerHTML.get) {\n patch_innerHTML(HTMLElement.prototype, Native.HTMLElement_innerHTML);\n } else {\n\n internals.addPatch(function(element) {\n patch_innerHTML(element, {\n enumerable: true,\n configurable: true,\n // Implements getting `innerHTML` by performing an unpatched `cloneNode`\n // of the element and returning the resulting element's `innerHTML`.\n // TODO: Is this too expensive?\n get: /** @this {Element} */ function() {\n return Native.Node_cloneNode.call(this, true).innerHTML;\n },\n // Implements setting `innerHTML` by creating an unpatched element,\n // setting `innerHTML` of that element and replacing the target\n // element's children with those of the unpatched element.\n set: /** @this {Element} */ function(assignedValue) {\n // NOTE: re-route to `content` for `template` elements.\n // We need to do this because `template.appendChild` does not\n // route into `template.content`.\n const isTemplate = (this.localName === 'template');\n /** @type {!Node} */\n const content = isTemplate ? (/** @type {!HTMLTemplateElement} */\n (this)).content : this;\n /** @type {!Node} */\n const rawElement = Native.Document_createElement.call(document,\n this.localName);\n rawElement.innerHTML = assignedValue;\n\n while (content.childNodes.length > 0) {\n Native.Node_removeChild.call(content, content.childNodes[0]);\n }\n const container = isTemplate ? rawElement.content : rawElement;\n while (container.childNodes.length > 0) {\n Native.Node_appendChild.call(content, container.childNodes[0]);\n }\n },\n });\n });\n }\n\n\n Utilities.setPropertyUnchecked(Element.prototype, 'setAttribute',\n /**\n * @this {Element}\n * @param {string} name\n * @param {string} newValue\n */\n function(name, newValue) {\n // Fast path for non-custom elements.\n if (this.__CE_state !== CEState.custom) {\n return Native.Element_setAttribute.call(this, name, newValue);\n }\n\n const oldValue = Native.Element_getAttribute.call(this, name);\n Native.Element_setAttribute.call(this, name, newValue);\n newValue = Native.Element_getAttribute.call(this, name);\n internals.attributeChangedCallback(this, name, oldValue, newValue, null);\n });\n\n Utilities.setPropertyUnchecked(Element.prototype, 'setAttributeNS',\n /**\n * @this {Element}\n * @param {?string} namespace\n * @param {string} name\n * @param {string} newValue\n */\n function(namespace, name, newValue) {\n // Fast path for non-custom elements.\n if (this.__CE_state !== CEState.custom) {\n return Native.Element_setAttributeNS.call(this, namespace, name, newValue);\n }\n\n const oldValue = Native.Element_getAttributeNS.call(this, namespace, name);\n Native.Element_setAttributeNS.call(this, namespace, name, newValue);\n newValue = Native.Element_getAttributeNS.call(this, namespace, name);\n internals.attributeChangedCallback(this, name, oldValue, newValue, namespace);\n });\n\n Utilities.setPropertyUnchecked(Element.prototype, 'removeAttribute',\n /**\n * @this {Element}\n * @param {string} name\n */\n function(name) {\n // Fast path for non-custom elements.\n if (this.__CE_state !== CEState.custom) {\n return Native.Element_removeAttribute.call(this, name);\n }\n\n const oldValue = Native.Element_getAttribute.call(this, name);\n Native.Element_removeAttribute.call(this, name);\n if (oldValue !== null) {\n internals.attributeChangedCallback(this, name, oldValue, null, null);\n }\n });\n\n Utilities.setPropertyUnchecked(Element.prototype, 'removeAttributeNS',\n /**\n * @this {Element}\n * @param {?string} namespace\n * @param {string} name\n */\n function(namespace, name) {\n // Fast path for non-custom elements.\n if (this.__CE_state !== CEState.custom) {\n return Native.Element_removeAttributeNS.call(this, namespace, name);\n }\n\n const oldValue = Native.Element_getAttributeNS.call(this, namespace, name);\n Native.Element_removeAttributeNS.call(this, namespace, name);\n // In older browsers, `Element#getAttributeNS` may return the empty string\n // instead of null if the attribute does not exist. For details, see;\n // https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttributeNS#Notes\n const newValue = Native.Element_getAttributeNS.call(this, namespace, name);\n if (oldValue !== newValue) {\n internals.attributeChangedCallback(this, name, oldValue, newValue, namespace);\n }\n });\n\n\n function patch_insertAdjacentElement(destination, baseMethod) {\n Utilities.setPropertyUnchecked(destination, 'insertAdjacentElement',\n /**\n * @this {Element}\n * @param {string} where\n * @param {!Element} element\n * @return {?Element}\n */\n function(where, element) {\n const wasConnected = Utilities.isConnected(element);\n const insertedElement = /** @type {!Element} */\n (baseMethod.call(this, where, element));\n\n if (wasConnected) {\n internals.disconnectTree(element);\n }\n\n if (Utilities.isConnected(insertedElement)) {\n internals.connectTree(element);\n }\n return insertedElement;\n });\n }\n\n if (Native.HTMLElement_insertAdjacentElement) {\n patch_insertAdjacentElement(HTMLElement.prototype, Native.HTMLElement_insertAdjacentElement);\n } else if (Native.Element_insertAdjacentElement) {\n patch_insertAdjacentElement(Element.prototype, Native.Element_insertAdjacentElement);\n } else {\n console.warn('Custom Elements: `Element#insertAdjacentElement` was not patched.');\n }\n\n\n PatchParentNode(internals, Element.prototype, {\n prepend: Native.Element_prepend,\n append: Native.Element_append,\n });\n\n PatchChildNode(internals, Element.prototype, {\n before: Native.Element_before,\n after: Native.Element_after,\n replaceWith: Native.Element_replaceWith,\n remove: Native.Element_remove,\n });\n};\n","import CustomElementInternals from '../CustomElementInternals.js';\nimport Native from './Native.js';\nimport PatchParentNode from './Interface/ParentNode.js';\n\n/**\n * @param {!CustomElementInternals} internals\n */\nexport default function(internals) {\n PatchParentNode(internals, DocumentFragment.prototype, {\n prepend: Native.DocumentFragment_prepend,\n append: Native.DocumentFragment_append,\n });\n};\n"]} \ No newline at end of file diff --git a/static/native-shim.js b/static/native-shim.js new file mode 100755 index 0000000..2011d29 --- /dev/null +++ b/static/native-shim.js @@ -0,0 +1,164 @@ +/** + * @license + * Copyright (c) 2016 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt + */ + +/** + * This shim allows elements written in, or compiled to, ES5 to work on native + * implementations of Custom Elements. + * + * ES5-style classes don't work with native Custom Elements because the + * HTMLElement constructor uses the value of `new.target` to look up the custom + * element definition for the currently called constructor. `new.target` is only + * set when `new` is called and is only propagated via super() calls. super() + * is not emulatable in ES5. The pattern of `SuperClass.call(this)`` only works + * when extending other ES5-style classes, and does not propagate `new.target`. + * + * This shim allows the native HTMLElement constructor to work by generating and + * registering a stand-in class instead of the users custom element class. This + * stand-in class's constructor has an actual call to super(). + * `customElements.define()` and `customElements.get()` are both overridden to + * hide this stand-in class from users. + * + * In order to create instance of the user-defined class, rather than the stand + * in, the stand-in's constructor swizzles its instances prototype and invokes + * the user-defined constructor. When the user-defined constructor is called + * directly it creates an instance of the stand-in class to get a real extension + * of HTMLElement and returns that. + * + * There are two important constructors: A patched HTMLElement constructor, and + * the StandInElement constructor. They both will be called to create an element + * but which is called first depends on whether the browser creates the element + * or the user-defined constructor is called directly. The variables + * `browserConstruction` and `userConstruction` control the flow between the + * two constructors. + * + * This shim should be better than forcing the polyfill because: + * 1. It's smaller + * 2. All reaction timings are the same as native (mostly synchronous) + * 3. All reaction triggering DOM operations are automatically supported + * + * There are some restrictions and requirements on ES5 constructors: + * 1. All constructors in a inheritance hierarchy must be ES5-style, so that + * they can be called with Function.call(). This effectively means that the + * whole application must be compiled to ES5. + * 2. Constructors must return the value of the emulated super() call. Like + * `return SuperClass.call(this)` + * 3. The `this` reference should not be used before the emulated super() call + * just like `this` is illegal to use before super() in ES6. + * 4. Constructors should not create other custom elements before the emulated + * super() call. This is the same restriction as with native custom + * elements. + * + * Compiling valid class-based custom elements to ES5 will satisfy these + * requirements with the latest version of popular transpilers. + */ +(() => { + 'use strict'; + + // Do nothing if `customElements` does not exist. + if (!window.customElements) return; + + const NativeHTMLElement = window.HTMLElement; + const nativeDefine = window.customElements.define; + const nativeGet = window.customElements.get; + + /** + * Map of user-provided constructors to tag names. + * + * @type {Map} + */ + const tagnameByConstructor = new Map(); + + /** + * Map of tag names to user-provided constructors. + * + * @type {Map} + */ + const constructorByTagname = new Map(); + + + /** + * Whether the constructors are being called by a browser process, ie parsing + * or createElement. + */ + let browserConstruction = false; + + /** + * Whether the constructors are being called by a user-space process, ie + * calling an element constructor. + */ + let userConstruction = false; + + window.HTMLElement = function() { + if (!browserConstruction) { + const tagname = tagnameByConstructor.get(this.constructor); + const fakeClass = nativeGet.call(window.customElements, tagname); + + // Make sure that the fake constructor doesn't call back to this constructor + userConstruction = true; + const instance = new (fakeClass)(); + return instance; + } + // Else do nothing. This will be reached by ES5-style classes doing + // HTMLElement.call() during initialization + browserConstruction = false; + }; + // By setting the patched HTMLElement's prototype property to the native + // HTMLElement's prototype we make sure that: + // document.createElement('a') instanceof HTMLElement + // works because instanceof uses HTMLElement.prototype, which is on the + // ptototype chain of built-in elements. + window.HTMLElement.prototype = NativeHTMLElement.prototype; + + const define = (tagname, elementClass) => { + const elementProto = elementClass.prototype; + const StandInElement = class extends NativeHTMLElement { + constructor() { + // Call the native HTMLElement constructor, this gives us the + // under-construction instance as `this`: + super(); + + // The prototype will be wrong up because the browser used our fake + // class, so fix it: + Object.setPrototypeOf(this, elementProto); + + if (!userConstruction) { + // Make sure that user-defined constructor bottom's out to a do-nothing + // HTMLElement() call + browserConstruction = true; + // Call the user-defined constructor on our instance: + elementClass.call(this); + } + userConstruction = false; + } + }; + const standInProto = StandInElement.prototype; + StandInElement.observedAttributes = elementClass.observedAttributes; + standInProto.connectedCallback = elementProto.connectedCallback; + standInProto.disconnectedCallback = elementProto.disconnectedCallback; + standInProto.attributeChangedCallback = elementProto.attributeChangedCallback; + standInProto.adoptedCallback = elementProto.adoptedCallback; + + tagnameByConstructor.set(elementClass, tagname); + constructorByTagname.set(tagname, elementClass); + nativeDefine.call(window.customElements, tagname, StandInElement); + }; + + const get = (tagname) => constructorByTagname.get(tagname); + + // Workaround for Safari bug where patching customElements can be lost, likely + // due to native wrapper garbage collection issue + Object.defineProperty(window, 'customElements', + {value: window.customElements, configurable: true, writable: true}); + Object.defineProperty(window.customElements, 'define', + {value: define, configurable: true, writable: true}); + Object.defineProperty(window.customElements, 'get', + {value: get, configurable: true, writable: true}); + +})(); diff --git a/static/prettify.js b/static/prettify.js new file mode 100755 index 0000000..10c9fb7 --- /dev/null +++ b/static/prettify.js @@ -0,0 +1,1655 @@ +// Copyright (C) 2006 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/** + * @fileoverview + * some functions for browser-side pretty printing of code contained in html. + * + *

+ * For a fairly comprehensive set of languages see the + * README + * file that came with this source. At a minimum, the lexer should work on a + * number of languages including C and friends, Java, Python, Bash, SQL, HTML, + * XML, CSS, Javascript, and Makefiles. It works passably on Ruby, PHP and Awk + * and a subset of Perl, but, because of commenting conventions, doesn't work on + * Smalltalk, Lisp-like, or CAML-like languages without an explicit lang class. + *

+ * Usage:

    + *
  1. include this source file in an html page via + * {@code } + *
  2. define style rules. See the example page for examples. + *
  3. mark the {@code
    } and {@code } tags in your source with
    + *    {@code class=prettyprint.}
    + *    You can also use the (html deprecated) {@code } tag, but the pretty
    + *    printer needs to do more substantial DOM manipulations to support that, so
    + *    some css styles may not be preserved.
    + * </ol>
    + * That's it.  I wanted to keep the API as simple as possible, so there's no
    + * need to specify which language the code is in, but if you wish, you can add
    + * another class to the {@code <pre>} or {@code <code>} element to specify the
    + * language, as in {@code <pre class="prettyprint lang-java">}.  Any class that
    + * starts with "lang-" followed by a file extension, specifies the file type.
    + * See the "lang-*.js" files in this directory for code that implements
    + * per-language file handlers.
    + * <p>
    + * Change log:<br>
    + * cbeust, 2006/08/22
    + * <blockquote>
    + *   Java annotations (start with "@") are now captured as literals ("lit")
    + * </blockquote>
    + * @requires console
    + */
    +
    +// JSLint declarations
    +/*global console, document, navigator, setTimeout, window, define */
    +
    +/** @define {boolean} */
    +var IN_GLOBAL_SCOPE = true;
    +
    +/**
    + * Split {@code prettyPrint} into multiple timeouts so as not to interfere with
    + * UI events.
    + * If set to {@code false}, {@code prettyPrint()} is synchronous.
    + */
    +window['PR_SHOULD_USE_CONTINUATION'] = true;
    +
    +/**
    + * Pretty print a chunk of code.
    + * @param {string} sourceCodeHtml The HTML to pretty print.
    + * @param {string} opt_langExtension The language name to use.
    + *     Typically, a filename extension like 'cpp' or 'java'.
    + * @param {number|boolean} opt_numberLines True to number lines,
    + *     or the 1-indexed number of the first line in sourceCodeHtml.
    + * @return {string} code as html, but prettier
    + */
    +var prettyPrintOne;
    +/**
    + * Find all the {@code <pre>} and {@code <code>} tags in the DOM with
    + * {@code class=prettyprint} and prettify them.
    + *
    + * @param {Function} opt_whenDone called when prettifying is done.
    + * @param {HTMLElement|HTMLDocument} opt_root an element or document
    + *   containing all the elements to pretty print.
    + *   Defaults to {@code document.body}.
    + */
    +var prettyPrint;
    +
    +
    +(function () {
    +  var win = window;
    +  // Keyword lists for various languages.
    +  // We use things that coerce to strings to make them compact when minified
    +  // and to defeat aggressive optimizers that fold large string constants.
    +  var FLOW_CONTROL_KEYWORDS = ["break,continue,do,else,for,if,return,while"];
    +  var C_KEYWORDS = [FLOW_CONTROL_KEYWORDS,"auto,case,char,const,default," + 
    +      "double,enum,extern,float,goto,inline,int,long,register,short,signed," +
    +      "sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];
    +  var COMMON_KEYWORDS = [C_KEYWORDS,"catch,class,delete,false,import," +
    +      "new,operator,private,protected,public,this,throw,true,try,typeof"];
    +  var CPP_KEYWORDS = [COMMON_KEYWORDS,"alignof,align_union,asm,axiom,bool," +
    +      "concept,concept_map,const_cast,constexpr,decltype,delegate," +
    +      "dynamic_cast,explicit,export,friend,generic,late_check," +
    +      "mutable,namespace,nullptr,property,reinterpret_cast,static_assert," +
    +      "static_cast,template,typeid,typename,using,virtual,where"];
    +  var JAVA_KEYWORDS = [COMMON_KEYWORDS,
    +      "abstract,assert,boolean,byte,extends,final,finally,implements,import," +
    +      "instanceof,interface,null,native,package,strictfp,super,synchronized," +
    +      "throws,transient"];
    +  var CSHARP_KEYWORDS = [JAVA_KEYWORDS,
    +      "as,base,by,checked,decimal,delegate,descending,dynamic,event," +
    +      "fixed,foreach,from,group,implicit,in,internal,into,is,let," +
    +      "lock,object,out,override,orderby,params,partial,readonly,ref,sbyte," +
    +      "sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort," +
    +      "var,virtual,where"];
    +  var COFFEE_KEYWORDS = "all,and,by,catch,class,else,extends,false,finally," +
    +      "for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then," +
    +      "throw,true,try,unless,until,when,while,yes";
    +  var JSCRIPT_KEYWORDS = [COMMON_KEYWORDS,
    +      "debugger,eval,export,function,get,null,set,undefined,var,with," +
    +      "Infinity,NaN"];
    +  var PERL_KEYWORDS = "caller,delete,die,do,dump,elsif,eval,exit,foreach,for," +
    +      "goto,if,import,last,local,my,next,no,our,print,package,redo,require," +
    +      "sub,undef,unless,until,use,wantarray,while,BEGIN,END";
    +  var PYTHON_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "and,as,assert,class,def,del," +
    +      "elif,except,exec,finally,from,global,import,in,is,lambda," +
    +      "nonlocal,not,or,pass,print,raise,try,with,yield," +
    +      "False,True,None"];
    +  var RUBY_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "alias,and,begin,case,class," +
    +      "def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo," +
    +      "rescue,retry,self,super,then,true,undef,unless,until,when,yield," +
    +      "BEGIN,END"];
    +   var RUST_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "as,assert,const,copy,drop," +
    +      "enum,extern,fail,false,fn,impl,let,log,loop,match,mod,move,mut,priv," +
    +      "pub,pure,ref,self,static,struct,true,trait,type,unsafe,use"];
    +  var SH_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "case,done,elif,esac,eval,fi," +
    +      "function,in,local,set,then,until"];
    +  var ALL_KEYWORDS = [
    +      CPP_KEYWORDS, CSHARP_KEYWORDS, JSCRIPT_KEYWORDS, PERL_KEYWORDS,
    +      PYTHON_KEYWORDS, RUBY_KEYWORDS, SH_KEYWORDS];
    +  var C_TYPES = /^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/;
    +
    +  // token style names.  correspond to css classes
    +  /**
    +   * token style for a string literal
    +   * @const
    +   */
    +  var PR_STRING = 'str';
    +  /**
    +   * token style for a keyword
    +   * @const
    +   */
    +  var PR_KEYWORD = 'kwd';
    +  /**
    +   * token style for a comment
    +   * @const
    +   */
    +  var PR_COMMENT = 'com';
    +  /**
    +   * token style for a type
    +   * @const
    +   */
    +  var PR_TYPE = 'typ';
    +  /**
    +   * token style for a literal value.  e.g. 1, null, true.
    +   * @const
    +   */
    +  var PR_LITERAL = 'lit';
    +  /**
    +   * token style for a punctuation string.
    +   * @const
    +   */
    +  var PR_PUNCTUATION = 'pun';
    +  /**
    +   * token style for plain text.
    +   * @const
    +   */
    +  var PR_PLAIN = 'pln';
    +
    +  /**
    +   * token style for an sgml tag.
    +   * @const
    +   */
    +  var PR_TAG = 'tag';
    +  /**
    +   * token style for a markup declaration such as a DOCTYPE.
    +   * @const
    +   */
    +  var PR_DECLARATION = 'dec';
    +  /**
    +   * token style for embedded source.
    +   * @const
    +   */
    +  var PR_SOURCE = 'src';
    +  /**
    +   * token style for an sgml attribute name.
    +   * @const
    +   */
    +  var PR_ATTRIB_NAME = 'atn';
    +  /**
    +   * token style for an sgml attribute value.
    +   * @const
    +   */
    +  var PR_ATTRIB_VALUE = 'atv';
    +
    +  /**
    +   * A class that indicates a section of markup that is not code, e.g. to allow
    +   * embedding of line numbers within code listings.
    +   * @const
    +   */
    +  var PR_NOCODE = 'nocode';
    +
    +  
    +  
    +  /**
    +   * A set of tokens that can precede a regular expression literal in
    +   * javascript
    +   * http://web.archive.org/web/20070717142515/http://www.mozilla.org/js/language/js20/rationale/syntax.html
    +   * has the full list, but I've removed ones that might be problematic when
    +   * seen in languages that don't support regular expression literals.
    +   *
    +   * <p>Specifically, I've removed any keywords that can't precede a regexp
    +   * literal in a syntactically legal javascript program, and I've removed the
    +   * "in" keyword since it's not a keyword in many languages, and might be used
    +   * as a count of inches.
    +   *
    +   * <p>The link above does not accurately describe EcmaScript rules since
    +   * it fails to distinguish between (a=++/b/i) and (a++/b/i) but it works
    +   * very well in practice.
    +   *
    +   * @private
    +   * @const
    +   */
    +  var REGEXP_PRECEDER_PATTERN = '(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<<?=?|>>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*';
    +  
    +  // CAVEAT: this does not properly handle the case where a regular
    +  // expression immediately follows another since a regular expression may
    +  // have flags for case-sensitivity and the like.  Having regexp tokens
    +  // adjacent is not valid in any language I'm aware of, so I'm punting.
    +  // TODO: maybe style special characters inside a regexp as punctuation.
    +
    +  /**
    +   * Given a group of {@link RegExp}s, returns a {@code RegExp} that globally
    +   * matches the union of the sets of strings matched by the input RegExp.
    +   * Since it matches globally, if the input strings have a start-of-input
    +   * anchor (/^.../), it is ignored for the purposes of unioning.
    +   * @param {Array.<RegExp>} regexs non multiline, non-global regexs.
    +   * @return {RegExp} a global regex.
    +   */
    +  function combinePrefixPatterns(regexs) {
    +    var capturedGroupIndex = 0;
    +  
    +    var needToFoldCase = false;
    +    var ignoreCase = false;
    +    for (var i = 0, n = regexs.length; i < n; ++i) {
    +      var regex = regexs[i];
    +      if (regex.ignoreCase) {
    +        ignoreCase = true;
    +      } else if (/[a-z]/i.test(regex.source.replace(
    +                     /\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi, ''))) {
    +        needToFoldCase = true;
    +        ignoreCase = false;
    +        break;
    +      }
    +    }
    +  
    +    var escapeCharToCodeUnit = {
    +      'b': 8,
    +      't': 9,
    +      'n': 0xa,
    +      'v': 0xb,
    +      'f': 0xc,
    +      'r': 0xd
    +    };
    +  
    +    function decodeEscape(charsetPart) {
    +      var cc0 = charsetPart.charCodeAt(0);
    +      if (cc0 !== 92 /* \\ */) {
    +        return cc0;
    +      }
    +      var c1 = charsetPart.charAt(1);
    +      cc0 = escapeCharToCodeUnit[c1];
    +      if (cc0) {
    +        return cc0;
    +      } else if ('0' <= c1 && c1 <= '7') {
    +        return parseInt(charsetPart.substring(1), 8);
    +      } else if (c1 === 'u' || c1 === 'x') {
    +        return parseInt(charsetPart.substring(2), 16);
    +      } else {
    +        return charsetPart.charCodeAt(1);
    +      }
    +    }
    +  
    +    function encodeEscape(charCode) {
    +      if (charCode < 0x20) {
    +        return (charCode < 0x10 ? '\\x0' : '\\x') + charCode.toString(16);
    +      }
    +      var ch = String.fromCharCode(charCode);
    +      return (ch === '\\' || ch === '-' || ch === ']' || ch === '^')
    +          ? "\\" + ch : ch;
    +    }
    +  
    +    function caseFoldCharset(charSet) {
    +      var charsetParts = charSet.substring(1, charSet.length - 1).match(
    +          new RegExp(
    +              '\\\\u[0-9A-Fa-f]{4}'
    +              + '|\\\\x[0-9A-Fa-f]{2}'
    +              + '|\\\\[0-3][0-7]{0,2}'
    +              + '|\\\\[0-7]{1,2}'
    +              + '|\\\\[\\s\\S]'
    +              + '|-'
    +              + '|[^-\\\\]',
    +              'g'));
    +      var ranges = [];
    +      var inverse = charsetParts[0] === '^';
    +  
    +      var out = ['['];
    +      if (inverse) { out.push('^'); }
    +  
    +      for (var i = inverse ? 1 : 0, n = charsetParts.length; i < n; ++i) {
    +        var p = charsetParts[i];
    +        if (/\\[bdsw]/i.test(p)) {  // Don't muck with named groups.
    +          out.push(p);
    +        } else {
    +          var start = decodeEscape(p);
    +          var end;
    +          if (i + 2 < n && '-' === charsetParts[i + 1]) {
    +            end = decodeEscape(charsetParts[i + 2]);
    +            i += 2;
    +          } else {
    +            end = start;
    +          }
    +          ranges.push([start, end]);
    +          // If the range might intersect letters, then expand it.
    +          // This case handling is too simplistic.
    +          // It does not deal with non-latin case folding.
    +          // It works for latin source code identifiers though.
    +          if (!(end < 65 || start > 122)) {
    +            if (!(end < 65 || start > 90)) {
    +              ranges.push([Math.max(65, start) | 32, Math.min(end, 90) | 32]);
    +            }
    +            if (!(end < 97 || start > 122)) {
    +              ranges.push([Math.max(97, start) & ~32, Math.min(end, 122) & ~32]);
    +            }
    +          }
    +        }
    +      }
    +  
    +      // [[1, 10], [3, 4], [8, 12], [14, 14], [16, 16], [17, 17]]
    +      // -> [[1, 12], [14, 14], [16, 17]]
    +      ranges.sort(function (a, b) { return (a[0] - b[0]) || (b[1]  - a[1]); });
    +      var consolidatedRanges = [];
    +      var lastRange = [];
    +      for (var i = 0; i < ranges.length; ++i) {
    +        var range = ranges[i];
    +        if (range[0] <= lastRange[1] + 1) {
    +          lastRange[1] = Math.max(lastRange[1], range[1]);
    +        } else {
    +          consolidatedRanges.push(lastRange = range);
    +        }
    +      }
    +  
    +      for (var i = 0; i < consolidatedRanges.length; ++i) {
    +        var range = consolidatedRanges[i];
    +        out.push(encodeEscape(range[0]));
    +        if (range[1] > range[0]) {
    +          if (range[1] + 1 > range[0]) { out.push('-'); }
    +          out.push(encodeEscape(range[1]));
    +        }
    +      }
    +      out.push(']');
    +      return out.join('');
    +    }
    +  
    +    function allowAnywhereFoldCaseAndRenumberGroups(regex) {
    +      // Split into character sets, escape sequences, punctuation strings
    +      // like ('(', '(?:', ')', '^'), and runs of characters that do not
    +      // include any of the above.
    +      var parts = regex.source.match(
    +          new RegExp(
    +              '(?:'
    +              + '\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]'  // a character set
    +              + '|\\\\u[A-Fa-f0-9]{4}'  // a unicode escape
    +              + '|\\\\x[A-Fa-f0-9]{2}'  // a hex escape
    +              + '|\\\\[0-9]+'  // a back-reference or octal escape
    +              + '|\\\\[^ux0-9]'  // other escape sequence
    +              + '|\\(\\?[:!=]'  // start of a non-capturing group
    +              + '|[\\(\\)\\^]'  // start/end of a group, or line start
    +              + '|[^\\x5B\\x5C\\(\\)\\^]+'  // run of other characters
    +              + ')',
    +              'g'));
    +      var n = parts.length;
    +  
    +      // Maps captured group numbers to the number they will occupy in
    +      // the output or to -1 if that has not been determined, or to
    +      // undefined if they need not be capturing in the output.
    +      var capturedGroups = [];
    +  
    +      // Walk over and identify back references to build the capturedGroups
    +      // mapping.
    +      for (var i = 0, groupIndex = 0; i < n; ++i) {
    +        var p = parts[i];
    +        if (p === '(') {
    +          // groups are 1-indexed, so max group index is count of '('
    +          ++groupIndex;
    +        } else if ('\\' === p.charAt(0)) {
    +          var decimalValue = +p.substring(1);
    +          if (decimalValue) {
    +            if (decimalValue <= groupIndex) {
    +              capturedGroups[decimalValue] = -1;
    +            } else {
    +              // Replace with an unambiguous escape sequence so that
    +              // an octal escape sequence does not turn into a backreference
    +              // to a capturing group from an earlier regex.
    +              parts[i] = encodeEscape(decimalValue);
    +            }
    +          }
    +        }
    +      }
    +  
    +      // Renumber groups and reduce capturing groups to non-capturing groups
    +      // where possible.
    +      for (var i = 1; i < capturedGroups.length; ++i) {
    +        if (-1 === capturedGroups[i]) {
    +          capturedGroups[i] = ++capturedGroupIndex;
    +        }
    +      }
    +      for (var i = 0, groupIndex = 0; i < n; ++i) {
    +        var p = parts[i];
    +        if (p === '(') {
    +          ++groupIndex;
    +          if (!capturedGroups[groupIndex]) {
    +            parts[i] = '(?:';
    +          }
    +        } else if ('\\' === p.charAt(0)) {
    +          var decimalValue = +p.substring(1);
    +          if (decimalValue && decimalValue <= groupIndex) {
    +            parts[i] = '\\' + capturedGroups[decimalValue];
    +          }
    +        }
    +      }
    +  
    +      // Remove any prefix anchors so that the output will match anywhere.
    +      // ^^ really does mean an anchored match though.
    +      for (var i = 0; i < n; ++i) {
    +        if ('^' === parts[i] && '^' !== parts[i + 1]) { parts[i] = ''; }
    +      }
    +  
    +      // Expand letters to groups to handle mixing of case-sensitive and
    +      // case-insensitive patterns if necessary.
    +      if (regex.ignoreCase && needToFoldCase) {
    +        for (var i = 0; i < n; ++i) {
    +          var p = parts[i];
    +          var ch0 = p.charAt(0);
    +          if (p.length >= 2 && ch0 === '[') {
    +            parts[i] = caseFoldCharset(p);
    +          } else if (ch0 !== '\\') {
    +            // TODO: handle letters in numeric escapes.
    +            parts[i] = p.replace(
    +                /[a-zA-Z]/g,
    +                function (ch) {
    +                  var cc = ch.charCodeAt(0);
    +                  return '[' + String.fromCharCode(cc & ~32, cc | 32) + ']';
    +                });
    +          }
    +        }
    +      }
    +  
    +      return parts.join('');
    +    }
    +  
    +    var rewritten = [];
    +    for (var i = 0, n = regexs.length; i < n; ++i) {
    +      var regex = regexs[i];
    +      if (regex.global || regex.multiline) { throw new Error('' + regex); }
    +      rewritten.push(
    +          '(?:' + allowAnywhereFoldCaseAndRenumberGroups(regex) + ')');
    +    }
    +  
    +    return new RegExp(rewritten.join('|'), ignoreCase ? 'gi' : 'g');
    +  }
    +
    +  /**
    +   * Split markup into a string of source code and an array mapping ranges in
    +   * that string to the text nodes in which they appear.
    +   *
    +   * <p>
    +   * The HTML DOM structure:</p>
    +   * <pre>
    +   * (Element   "p"
    +   *   (Element "b"
    +   *     (Text  "print "))       ; #1
    +   *   (Text    "'Hello '")      ; #2
    +   *   (Element "br")            ; #3
    +   *   (Text    "  + 'World';")) ; #4
    +   * </pre>
    +   * <p>
    +   * corresponds to the HTML
    +   * {@code <p><b>print </b>'Hello '<br>  + 'World';</p>}.</p>
    +   *
    +   * <p>
    +   * It will produce the output:</p>
    +   * <pre>
    +   * {
    +   *   sourceCode: "print 'Hello '\n  + 'World';",
    +   *   //                     1          2
    +   *   //           012345678901234 5678901234567
    +   *   spans: [0, #1, 6, #2, 14, #3, 15, #4]
    +   * }
    +   * </pre>
    +   * <p>
    +   * where #1 is a reference to the {@code "print "} text node above, and so
    +   * on for the other text nodes.
    +   * </p>
    +   *
    +   * <p>
    +   * The {@code} spans array is an array of pairs.  Even elements are the start
    +   * indices of substrings, and odd elements are the text nodes (or BR elements)
    +   * that contain the text for those substrings.
    +   * Substrings continue until the next index or the end of the source.
    +   * </p>
    +   *
    +   * @param {Node} node an HTML DOM subtree containing source-code.
    +   * @param {boolean} isPreformatted true if white-space in text nodes should
    +   *    be considered significant.
    +   * @return {Object} source code and the text nodes in which they occur.
    +   */
    +  function extractSourceSpans(node, isPreformatted) {
    +    var nocode = /(?:^|\s)nocode(?:\s|$)/;
    +  
    +    var chunks = [];
    +    var length = 0;
    +    var spans = [];
    +    var k = 0;
    +  
    +    function walk(node) {
    +      var type = node.nodeType;
    +      if (type == 1) {  // Element
    +        if (nocode.test(node.className)) { return; }
    +        for (var child = node.firstChild; child; child = child.nextSibling) {
    +          walk(child);
    +        }
    +        var nodeName = node.nodeName.toLowerCase();
    +        if ('br' === nodeName || 'li' === nodeName) {
    +          chunks[k] = '\n';
    +          spans[k << 1] = length++;
    +          spans[(k++ << 1) | 1] = node;
    +        }
    +      } else if (type == 3 || type == 4) {  // Text
    +        var text = node.nodeValue;
    +        if (text.length) {
    +          if (!isPreformatted) {
    +            text = text.replace(/[ \t\r\n]+/g, ' ');
    +          } else {
    +            text = text.replace(/\r\n?/g, '\n');  // Normalize newlines.
    +          }
    +          // TODO: handle tabs here?
    +          chunks[k] = text;
    +          spans[k << 1] = length;
    +          length += text.length;
    +          spans[(k++ << 1) | 1] = node;
    +        }
    +      }
    +    }
    +  
    +    walk(node);
    +  
    +    return {
    +      sourceCode: chunks.join('').replace(/\n$/, ''),
    +      spans: spans
    +    };
    +  }
    +
    +  /**
    +   * Apply the given language handler to sourceCode and add the resulting
    +   * decorations to out.
    +   * @param {number} basePos the index of sourceCode within the chunk of source
    +   *    whose decorations are already present on out.
    +   */
    +  function appendDecorations(basePos, sourceCode, langHandler, out) {
    +    if (!sourceCode) { return; }
    +    var job = {
    +      sourceCode: sourceCode,
    +      basePos: basePos
    +    };
    +    langHandler(job);
    +    out.push.apply(out, job.decorations);
    +  }
    +
    +  var notWs = /\S/;
    +
    +  /**
    +   * Given an element, if it contains only one child element and any text nodes
    +   * it contains contain only space characters, return the sole child element.
    +   * Otherwise returns undefined.
    +   * <p>
    +   * This is meant to return the CODE element in {@code <pre><code ...>} when
    +   * there is a single child element that contains all the non-space textual
    +   * content, but not to return anything where there are multiple child elements
    +   * as in {@code <pre><code>...</code><code>...</code></pre>} or when there
    +   * is textual content.
    +   */
    +  function childContentWrapper(element) {
    +    var wrapper = undefined;
    +    for (var c = element.firstChild; c; c = c.nextSibling) {
    +      var type = c.nodeType;
    +      wrapper = (type === 1)  // Element Node
    +          ? (wrapper ? element : c)
    +          : (type === 3)  // Text Node
    +          ? (notWs.test(c.nodeValue) ? element : wrapper)
    +          : wrapper;
    +    }
    +    return wrapper === element ? undefined : wrapper;
    +  }
    +
    +  /** Given triples of [style, pattern, context] returns a lexing function,
    +    * The lexing function interprets the patterns to find token boundaries and
    +    * returns a decoration list of the form
    +    * [index_0, style_0, index_1, style_1, ..., index_n, style_n]
    +    * where index_n is an index into the sourceCode, and style_n is a style
    +    * constant like PR_PLAIN.  index_n-1 <= index_n, and style_n-1 applies to
    +    * all characters in sourceCode[index_n-1:index_n].
    +    *
    +    * The stylePatterns is a list whose elements have the form
    +    * [style : string, pattern : RegExp, DEPRECATED, shortcut : string].
    +    *
    +    * Style is a style constant like PR_PLAIN, or can be a string of the
    +    * form 'lang-FOO', where FOO is a language extension describing the
    +    * language of the portion of the token in $1 after pattern executes.
    +    * E.g., if style is 'lang-lisp', and group 1 contains the text
    +    * '(hello (world))', then that portion of the token will be passed to the
    +    * registered lisp handler for formatting.
    +    * The text before and after group 1 will be restyled using this decorator
    +    * so decorators should take care that this doesn't result in infinite
    +    * recursion.  For example, the HTML lexer rule for SCRIPT elements looks
    +    * something like ['lang-js', /<[s]cript>(.+?)<\/script>/].  This may match
    +    * '<script>foo()<\/script>', which would cause the current decorator to
    +    * be called with '<script>' which would not match the same rule since
    +    * group 1 must not be empty, so it would be instead styled as PR_TAG by
    +    * the generic tag rule.  The handler registered for the 'js' extension would
    +    * then be called with 'foo()', and finally, the current decorator would
    +    * be called with '<\/script>' which would not match the original rule and
    +    * so the generic tag rule would identify it as a tag.
    +    *
    +    * Pattern must only match prefixes, and if it matches a prefix, then that
    +    * match is considered a token with the same style.
    +    *
    +    * Context is applied to the last non-whitespace, non-comment token
    +    * recognized.
    +    *
    +    * Shortcut is an optional string of characters, any of which, if the first
    +    * character, gurantee that this pattern and only this pattern matches.
    +    *
    +    * @param {Array} shortcutStylePatterns patterns that always start with
    +    *   a known character.  Must have a shortcut string.
    +    * @param {Array} fallthroughStylePatterns patterns that will be tried in
    +    *   order if the shortcut ones fail.  May have shortcuts.
    +    *
    +    * @return {function (Object)} a
    +    *   function that takes source code and returns a list of decorations.
    +    */
    +  function createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns) {
    +    var shortcuts = {};
    +    var tokenizer;
    +    (function () {
    +      var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns);
    +      var allRegexs = [];
    +      var regexKeys = {};
    +      for (var i = 0, n = allPatterns.length; i < n; ++i) {
    +        var patternParts = allPatterns[i];
    +        var shortcutChars = patternParts[3];
    +        if (shortcutChars) {
    +          for (var c = shortcutChars.length; --c >= 0;) {
    +            shortcuts[shortcutChars.charAt(c)] = patternParts;
    +          }
    +        }
    +        var regex = patternParts[1];
    +        var k = '' + regex;
    +        if (!regexKeys.hasOwnProperty(k)) {
    +          allRegexs.push(regex);
    +          regexKeys[k] = null;
    +        }
    +      }
    +      allRegexs.push(/[\0-\uffff]/);
    +      tokenizer = combinePrefixPatterns(allRegexs);
    +    })();
    +
    +    var nPatterns = fallthroughStylePatterns.length;
    +
    +    /**
    +     * Lexes job.sourceCode and produces an output array job.decorations of
    +     * style classes preceded by the position at which they start in
    +     * job.sourceCode in order.
    +     *
    +     * @param {Object} job an object like <pre>{
    +     *    sourceCode: {string} sourceText plain text,
    +     *    basePos: {int} position of job.sourceCode in the larger chunk of
    +     *        sourceCode.
    +     * }</pre>
    +     */
    +    var decorate = function (job) {
    +      var sourceCode = job.sourceCode, basePos = job.basePos;
    +      /** Even entries are positions in source in ascending order.  Odd enties
    +        * are style markers (e.g., PR_COMMENT) that run from that position until
    +        * the end.
    +        * @type {Array.<number|string>}
    +        */
    +      var decorations = [basePos, PR_PLAIN];
    +      var pos = 0;  // index into sourceCode
    +      var tokens = sourceCode.match(tokenizer) || [];
    +      var styleCache = {};
    +
    +      for (var ti = 0, nTokens = tokens.length; ti < nTokens; ++ti) {
    +        var token = tokens[ti];
    +        var style = styleCache[token];
    +        var match = void 0;
    +
    +        var isEmbedded;
    +        if (typeof style === 'string') {
    +          isEmbedded = false;
    +        } else {
    +          var patternParts = shortcuts[token.charAt(0)];
    +          if (patternParts) {
    +            match = token.match(patternParts[1]);
    +            style = patternParts[0];
    +          } else {
    +            for (var i = 0; i < nPatterns; ++i) {
    +              patternParts = fallthroughStylePatterns[i];
    +              match = token.match(patternParts[1]);
    +              if (match) {
    +                style = patternParts[0];
    +                break;
    +              }
    +            }
    +
    +            if (!match) {  // make sure that we make progress
    +              style = PR_PLAIN;
    +            }
    +          }
    +
    +          isEmbedded = style.length >= 5 && 'lang-' === style.substring(0, 5);
    +          if (isEmbedded && !(match && typeof match[1] === 'string')) {
    +            isEmbedded = false;
    +            style = PR_SOURCE;
    +          }
    +
    +          if (!isEmbedded) { styleCache[token] = style; }
    +        }
    +
    +        var tokenStart = pos;
    +        pos += token.length;
    +
    +        if (!isEmbedded) {
    +          decorations.push(basePos + tokenStart, style);
    +        } else {  // Treat group 1 as an embedded block of source code.
    +          var embeddedSource = match[1];
    +          var embeddedSourceStart = token.indexOf(embeddedSource);
    +          var embeddedSourceEnd = embeddedSourceStart + embeddedSource.length;
    +          if (match[2]) {
    +            // If embeddedSource can be blank, then it would match at the
    +            // beginning which would cause us to infinitely recurse on the
    +            // entire token, so we catch the right context in match[2].
    +            embeddedSourceEnd = token.length - match[2].length;
    +            embeddedSourceStart = embeddedSourceEnd - embeddedSource.length;
    +          }
    +          var lang = style.substring(5);
    +          // Decorate the left of the embedded source
    +          appendDecorations(
    +              basePos + tokenStart,
    +              token.substring(0, embeddedSourceStart),
    +              decorate, decorations);
    +          // Decorate the embedded source
    +          appendDecorations(
    +              basePos + tokenStart + embeddedSourceStart,
    +              embeddedSource,
    +              langHandlerForExtension(lang, embeddedSource),
    +              decorations);
    +          // Decorate the right of the embedded section
    +          appendDecorations(
    +              basePos + tokenStart + embeddedSourceEnd,
    +              token.substring(embeddedSourceEnd),
    +              decorate, decorations);
    +        }
    +      }
    +      job.decorations = decorations;
    +    };
    +    return decorate;
    +  }
    +
    +  /** returns a function that produces a list of decorations from source text.
    +    *
    +    * This code treats ", ', and ` as string delimiters, and \ as a string
    +    * escape.  It does not recognize perl's qq() style strings.
    +    * It has no special handling for double delimiter escapes as in basic, or
    +    * the tripled delimiters used in python, but should work on those regardless
    +    * although in those cases a single string literal may be broken up into
    +    * multiple adjacent string literals.
    +    *
    +    * It recognizes C, C++, and shell style comments.
    +    *
    +    * @param {Object} options a set of optional parameters.
    +    * @return {function (Object)} a function that examines the source code
    +    *     in the input job and builds the decoration list.
    +    */
    +  function sourceDecorator(options) {
    +    var shortcutStylePatterns = [], fallthroughStylePatterns = [];
    +    if (options['tripleQuotedStrings']) {
    +      // '''multi-line-string''', 'single-line-string', and double-quoted
    +      shortcutStylePatterns.push(
    +          [PR_STRING,  /^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
    +           null, '\'"']);
    +    } else if (options['multiLineStrings']) {
    +      // 'multi-line-string', "multi-line-string"
    +      shortcutStylePatterns.push(
    +          [PR_STRING,  /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,
    +           null, '\'"`']);
    +    } else {
    +      // 'single-line-string', "single-line-string"
    +      shortcutStylePatterns.push(
    +          [PR_STRING,
    +           /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,
    +           null, '"\'']);
    +    }
    +    if (options['verbatimStrings']) {
    +      // verbatim-string-literal production from the C# grammar.  See issue 93.
    +      fallthroughStylePatterns.push(
    +          [PR_STRING, /^@\"(?:[^\"]|\"\")*(?:\"|$)/, null]);
    +    }
    +    var hc = options['hashComments'];
    +    if (hc) {
    +      if (options['cStyleComments']) {
    +        if (hc > 1) {  // multiline hash comments
    +          shortcutStylePatterns.push(
    +              [PR_COMMENT, /^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/, null, '#']);
    +        } else {
    +          // Stop C preprocessor declarations at an unclosed open comment
    +          shortcutStylePatterns.push(
    +              [PR_COMMENT, /^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\r\n]*)/,
    +               null, '#']);
    +        }
    +        // #include <stdio.h>
    +        fallthroughStylePatterns.push(
    +            [PR_STRING,
    +             /^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,
    +             null]);
    +      } else {
    +        shortcutStylePatterns.push([PR_COMMENT, /^#[^\r\n]*/, null, '#']);
    +      }
    +    }
    +    if (options['cStyleComments']) {
    +      fallthroughStylePatterns.push([PR_COMMENT, /^\/\/[^\r\n]*/, null]);
    +      fallthroughStylePatterns.push(
    +          [PR_COMMENT, /^\/\*[\s\S]*?(?:\*\/|$)/, null]);
    +    }
    +    var regexLiterals = options['regexLiterals'];
    +    if (regexLiterals) {
    +      /**
    +       * @const
    +       */
    +      var regexExcls = regexLiterals > 1
    +        ? ''  // Multiline regex literals
    +        : '\n\r';
    +      /**
    +       * @const
    +       */
    +      var regexAny = regexExcls ? '.' : '[\\S\\s]';
    +      /**
    +       * @const
    +       */
    +      var REGEX_LITERAL = (
    +          // A regular expression literal starts with a slash that is
    +          // not followed by * or / so that it is not confused with
    +          // comments.
    +          '/(?=[^/*' + regexExcls + '])'
    +          // and then contains any number of raw characters,
    +          + '(?:[^/\\x5B\\x5C' + regexExcls + ']'
    +          // escape sequences (\x5C),
    +          +    '|\\x5C' + regexAny
    +          // or non-nesting character sets (\x5B\x5D);
    +          +    '|\\x5B(?:[^\\x5C\\x5D' + regexExcls + ']'
    +          +             '|\\x5C' + regexAny + ')*(?:\\x5D|$))+'
    +          // finally closed by a /.
    +          + '/');
    +      fallthroughStylePatterns.push(
    +          ['lang-regex',
    +           RegExp('^' + REGEXP_PRECEDER_PATTERN + '(' + REGEX_LITERAL + ')')
    +           ]);
    +    }
    +
    +    var types = options['types'];
    +    if (types) {
    +      fallthroughStylePatterns.push([PR_TYPE, types]);
    +    }
    +
    +    var keywords = ("" + options['keywords']).replace(/^ | $/g, '');
    +    if (keywords.length) {
    +      fallthroughStylePatterns.push(
    +          [PR_KEYWORD,
    +           new RegExp('^(?:' + keywords.replace(/[\s,]+/g, '|') + ')\\b'),
    +           null]);
    +    }
    +
    +    shortcutStylePatterns.push([PR_PLAIN,       /^\s+/, null, ' \r\n\t\xA0']);
    +
    +    var punctuation =
    +      // The Bash man page says
    +
    +      // A word is a sequence of characters considered as a single
    +      // unit by GRUB. Words are separated by metacharacters,
    +      // which are the following plus space, tab, and newline: { }
    +      // | & $ ; < >
    +      // ...
    +      
    +      // A word beginning with # causes that word and all remaining
    +      // characters on that line to be ignored.
    +
    +      // which means that only a '#' after /(?:^|[{}|&$;<>\s])/ starts a
    +      // comment but empirically
    +      // $ echo {#}
    +      // {#}
    +      // $ echo \$#
    +      // $#
    +      // $ echo }#
    +      // }#
    +
    +      // so /(?:^|[|&;<>\s])/ is more appropriate.
    +
    +      // http://gcc.gnu.org/onlinedocs/gcc-2.95.3/cpp_1.html#SEC3
    +      // suggests that this definition is compatible with a
    +      // default mode that tries to use a single token definition
    +      // to recognize both bash/python style comments and C
    +      // preprocessor directives.
    +
    +      // This definition of punctuation does not include # in the list of
    +      // follow-on exclusions, so # will not be broken before if preceeded
    +      // by a punctuation character.  We could try to exclude # after
    +      // [|&;<>] but that doesn't seem to cause many major problems.
    +      // If that does turn out to be a problem, we should change the below
    +      // when hc is truthy to include # in the run of punctuation characters
    +      // only when not followint [|&;<>].
    +      '^.[^\\s\\w.$@\'"`/\\\\]*';
    +    if (options['regexLiterals']) {
    +      punctuation += '(?!\s*\/)';
    +    }
    +
    +    fallthroughStylePatterns.push(
    +        // TODO(mikesamuel): recognize non-latin letters and numerals in idents
    +        [PR_LITERAL,     /^@[a-z_$][a-z_$@0-9]*/i, null],
    +        [PR_TYPE,        /^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/, null],
    +        [PR_PLAIN,       /^[a-z_$][a-z_$@0-9]*/i, null],
    +        [PR_LITERAL,
    +         new RegExp(
    +             '^(?:'
    +             // A hex number
    +             + '0x[a-f0-9]+'
    +             // or an octal or decimal number,
    +             + '|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)'
    +             // possibly in scientific notation
    +             + '(?:e[+\\-]?\\d+)?'
    +             + ')'
    +             // with an optional modifier like UL for unsigned long
    +             + '[a-z]*', 'i'),
    +         null, '0123456789'],
    +        // Don't treat escaped quotes in bash as starting strings.
    +        // See issue 144.
    +        [PR_PLAIN,       /^\\[\s\S]?/, null],
    +        [PR_PUNCTUATION, new RegExp(punctuation), null]);
    +
    +    return createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns);
    +  }
    +
    +  var decorateSource = sourceDecorator({
    +        'keywords': ALL_KEYWORDS,
    +        'hashComments': true,
    +        'cStyleComments': true,
    +        'multiLineStrings': true,
    +        'regexLiterals': true
    +      });
    +
    +  /**
    +   * Given a DOM subtree, wraps it in a list, and puts each line into its own
    +   * list item.
    +   *
    +   * @param {Node} node modified in place.  Its content is pulled into an
    +   *     HTMLOListElement, and each line is moved into a separate list item.
    +   *     This requires cloning elements, so the input might not have unique
    +   *     IDs after numbering.
    +   * @param {boolean} isPreformatted true iff white-space in text nodes should
    +   *     be treated as significant.
    +   */
    +  function numberLines(node, opt_startLineNum, isPreformatted) {
    +    var nocode = /(?:^|\s)nocode(?:\s|$)/;
    +    var lineBreak = /\r\n?|\n/;
    +  
    +    var document = node.ownerDocument;
    +  
    +    var li = document.createElement('li');
    +    while (node.firstChild) {
    +      li.appendChild(node.firstChild);
    +    }
    +    // An array of lines.  We split below, so this is initialized to one
    +    // un-split line.
    +    var listItems = [li];
    +  
    +    function walk(node) {
    +      var type = node.nodeType;
    +      if (type == 1 && !nocode.test(node.className)) {  // Element
    +        if ('br' === node.nodeName) {
    +          breakAfter(node);
    +          // Discard the <BR> since it is now flush against a </LI>.
    +          if (node.parentNode) {
    +            node.parentNode.removeChild(node);
    +          }
    +        } else {
    +          for (var child = node.firstChild; child; child = child.nextSibling) {
    +            walk(child);
    +          }
    +        }
    +      } else if ((type == 3 || type == 4) && isPreformatted) {  // Text
    +        var text = node.nodeValue;
    +        var match = text.match(lineBreak);
    +        if (match) {
    +          var firstLine = text.substring(0, match.index);
    +          node.nodeValue = firstLine;
    +          var tail = text.substring(match.index + match[0].length);
    +          if (tail) {
    +            var parent = node.parentNode;
    +            parent.insertBefore(
    +              document.createTextNode(tail), node.nextSibling);
    +          }
    +          breakAfter(node);
    +          if (!firstLine) {
    +            // Don't leave blank text nodes in the DOM.
    +            node.parentNode.removeChild(node);
    +          }
    +        }
    +      }
    +    }
    +  
    +    // Split a line after the given node.
    +    function breakAfter(lineEndNode) {
    +      // If there's nothing to the right, then we can skip ending the line
    +      // here, and move root-wards since splitting just before an end-tag
    +      // would require us to create a bunch of empty copies.
    +      while (!lineEndNode.nextSibling) {
    +        lineEndNode = lineEndNode.parentNode;
    +        if (!lineEndNode) { return; }
    +      }
    +  
    +      function breakLeftOf(limit, copy) {
    +        // Clone shallowly if this node needs to be on both sides of the break.
    +        var rightSide = copy ? limit.cloneNode(false) : limit;
    +        var parent = limit.parentNode;
    +        if (parent) {
    +          // We clone the parent chain.
    +          // This helps us resurrect important styling elements that cross lines.
    +          // E.g. in <i>Foo<br>Bar</i>
    +          // should be rewritten to <li><i>Foo</i></li><li><i>Bar</i></li>.
    +          var parentClone = breakLeftOf(parent, 1);
    +          // Move the clone and everything to the right of the original
    +          // onto the cloned parent.
    +          var next = limit.nextSibling;
    +          parentClone.appendChild(rightSide);
    +          for (var sibling = next; sibling; sibling = next) {
    +            next = sibling.nextSibling;
    +            parentClone.appendChild(sibling);
    +          }
    +        }
    +        return rightSide;
    +      }
    +  
    +      var copiedListItem = breakLeftOf(lineEndNode.nextSibling, 0);
    +  
    +      // Walk the parent chain until we reach an unattached LI.
    +      for (var parent;
    +           // Check nodeType since IE invents document fragments.
    +           (parent = copiedListItem.parentNode) && parent.nodeType === 1;) {
    +        copiedListItem = parent;
    +      }
    +      // Put it on the list of lines for later processing.
    +      listItems.push(copiedListItem);
    +    }
    +  
    +    // Split lines while there are lines left to split.
    +    for (var i = 0;  // Number of lines that have been split so far.
    +         i < listItems.length;  // length updated by breakAfter calls.
    +         ++i) {
    +      walk(listItems[i]);
    +    }
    +  
    +    // Make sure numeric indices show correctly.
    +    if (opt_startLineNum === (opt_startLineNum|0)) {
    +      listItems[0].setAttribute('value', opt_startLineNum);
    +    }
    +  
    +    var ol = document.createElement('ol');
    +    ol.className = 'linenums';
    +    var offset = Math.max(0, ((opt_startLineNum - 1 /* zero index */)) | 0) || 0;
    +    for (var i = 0, n = listItems.length; i < n; ++i) {
    +      li = listItems[i];
    +      // Stick a class on the LIs so that stylesheets can
    +      // color odd/even rows, or any other row pattern that
    +      // is co-prime with 10.
    +      li.className = 'L' + ((i + offset) % 10);
    +      if (!li.firstChild) {
    +        li.appendChild(document.createTextNode('\xA0'));
    +      }
    +      ol.appendChild(li);
    +    }
    +  
    +    node.appendChild(ol);
    +  }
    +  /**
    +   * Breaks {@code job.sourceCode} around style boundaries in
    +   * {@code job.decorations} and modifies {@code job.sourceNode} in place.
    +   * @param {Object} job like <pre>{
    +   *    sourceCode: {string} source as plain text,
    +   *    sourceNode: {HTMLElement} the element containing the source,
    +   *    spans: {Array.<number|Node>} alternating span start indices into source
    +   *       and the text node or element (e.g. {@code <BR>}) corresponding to that
    +   *       span.
    +   *    decorations: {Array.<number|string} an array of style classes preceded
    +   *       by the position at which they start in job.sourceCode in order
    +   * }</pre>
    +   * @private
    +   */
    +  function recombineTagsAndDecorations(job) {
    +    var isIE8OrEarlier = /\bMSIE\s(\d+)/.exec(navigator.userAgent);
    +    isIE8OrEarlier = isIE8OrEarlier && +isIE8OrEarlier[1] <= 8;
    +    var newlineRe = /\n/g;
    +  
    +    var source = job.sourceCode;
    +    var sourceLength = source.length;
    +    // Index into source after the last code-unit recombined.
    +    var sourceIndex = 0;
    +  
    +    var spans = job.spans;
    +    var nSpans = spans.length;
    +    // Index into spans after the last span which ends at or before sourceIndex.
    +    var spanIndex = 0;
    +  
    +    var decorations = job.decorations;
    +    var nDecorations = decorations.length;
    +    // Index into decorations after the last decoration which ends at or before
    +    // sourceIndex.
    +    var decorationIndex = 0;
    +  
    +    // Remove all zero-length decorations.
    +    decorations[nDecorations] = sourceLength;
    +    var decPos, i;
    +    for (i = decPos = 0; i < nDecorations;) {
    +      if (decorations[i] !== decorations[i + 2]) {
    +        decorations[decPos++] = decorations[i++];
    +        decorations[decPos++] = decorations[i++];
    +      } else {
    +        i += 2;
    +      }
    +    }
    +    nDecorations = decPos;
    +  
    +    // Simplify decorations.
    +    for (i = decPos = 0; i < nDecorations;) {
    +      var startPos = decorations[i];
    +      // Conflate all adjacent decorations that use the same style.
    +      var startDec = decorations[i + 1];
    +      var end = i + 2;
    +      while (end + 2 <= nDecorations && decorations[end + 1] === startDec) {
    +        end += 2;
    +      }
    +      decorations[decPos++] = startPos;
    +      decorations[decPos++] = startDec;
    +      i = end;
    +    }
    +  
    +    nDecorations = decorations.length = decPos;
    +  
    +    var sourceNode = job.sourceNode;
    +    var oldDisplay;
    +    if (sourceNode) {
    +      oldDisplay = sourceNode.style.display;
    +      sourceNode.style.display = 'none';
    +    }
    +    try {
    +      var decoration = null;
    +      while (spanIndex < nSpans) {
    +        var spanStart = spans[spanIndex];
    +        var spanEnd = spans[spanIndex + 2] || sourceLength;
    +  
    +        var decEnd = decorations[decorationIndex + 2] || sourceLength;
    +  
    +        var end = Math.min(spanEnd, decEnd);
    +  
    +        var textNode = spans[spanIndex + 1];
    +        var styledText;
    +        if (textNode.nodeType !== 1  // Don't muck with <BR>s or <LI>s
    +            // Don't introduce spans around empty text nodes.
    +            && (styledText = source.substring(sourceIndex, end))) {
    +          // This may seem bizarre, and it is.  Emitting LF on IE causes the
    +          // code to display with spaces instead of line breaks.
    +          // Emitting Windows standard issue linebreaks (CRLF) causes a blank
    +          // space to appear at the beginning of every line but the first.
    +          // Emitting an old Mac OS 9 line separator makes everything spiffy.
    +          if (isIE8OrEarlier) {
    +            styledText = styledText.replace(newlineRe, '\r');
    +          }
    +          textNode.nodeValue = styledText;
    +          var document = textNode.ownerDocument;
    +          var span = document.createElement('span');
    +          span.className = decorations[decorationIndex + 1];
    +          var parentNode = textNode.parentNode;
    +          parentNode.replaceChild(span, textNode);
    +          span.appendChild(textNode);
    +          if (sourceIndex < spanEnd) {  // Split off a text node.
    +            spans[spanIndex + 1] = textNode
    +                // TODO: Possibly optimize by using '' if there's no flicker.
    +                = document.createTextNode(source.substring(end, spanEnd));
    +            parentNode.insertBefore(textNode, span.nextSibling);
    +          }
    +        }
    +  
    +        sourceIndex = end;
    +  
    +        if (sourceIndex >= spanEnd) {
    +          spanIndex += 2;
    +        }
    +        if (sourceIndex >= decEnd) {
    +          decorationIndex += 2;
    +        }
    +      }
    +    } finally {
    +      if (sourceNode) {
    +        sourceNode.style.display = oldDisplay;
    +      }
    +    }
    +  }
    +
    +  /** Maps language-specific file extensions to handlers. */
    +  var langHandlerRegistry = {};
    +  /** Register a language handler for the given file extensions.
    +    * @param {function (Object)} handler a function from source code to a list
    +    *      of decorations.  Takes a single argument job which describes the
    +    *      state of the computation.   The single parameter has the form
    +    *      {@code {
    +    *        sourceCode: {string} as plain text.
    +    *        decorations: {Array.<number|string>} an array of style classes
    +    *                     preceded by the position at which they start in
    +    *                     job.sourceCode in order.
    +    *                     The language handler should assigned this field.
    +    *        basePos: {int} the position of source in the larger source chunk.
    +    *                 All positions in the output decorations array are relative
    +    *                 to the larger source chunk.
    +    *      } }
    +    * @param {Array.<string>} fileExtensions
    +    */
    +  function registerLangHandler(handler, fileExtensions) {
    +    for (var i = fileExtensions.length; --i >= 0;) {
    +      var ext = fileExtensions[i];
    +      if (!langHandlerRegistry.hasOwnProperty(ext)) {
    +        langHandlerRegistry[ext] = handler;
    +      } else if (win['console']) {
    +        console['warn']('cannot override language handler %s', ext);
    +      }
    +    }
    +  }
    +  function langHandlerForExtension(extension, source) {
    +    if (!(extension && langHandlerRegistry.hasOwnProperty(extension))) {
    +      // Treat it as markup if the first non whitespace character is a < and
    +      // the last non-whitespace character is a >.
    +      extension = /^\s*</.test(source)
    +          ? 'default-markup'
    +          : 'default-code';
    +    }
    +    return langHandlerRegistry[extension];
    +  }
    +  registerLangHandler(decorateSource, ['default-code']);
    +  registerLangHandler(
    +      createSimpleLexer(
    +          [],
    +          [
    +           [PR_PLAIN,       /^[^<?]+/],
    +           [PR_DECLARATION, /^<!\w[^>]*(?:>|$)/],
    +           [PR_COMMENT,     /^<\!--[\s\S]*?(?:-\->|$)/],
    +           // Unescaped content in an unknown language
    +           ['lang-',        /^<\?([\s\S]+?)(?:\?>|$)/],
    +           ['lang-',        /^<%([\s\S]+?)(?:%>|$)/],
    +           [PR_PUNCTUATION, /^(?:<[%?]|[%?]>)/],
    +           ['lang-',        /^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],
    +           // Unescaped content in javascript.  (Or possibly vbscript).
    +           ['lang-js',      /^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],
    +           // Contains unescaped stylesheet content
    +           ['lang-css',     /^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],
    +           ['lang-in.tag',  /^(<\/?[a-z][^<>]*>)/i]
    +          ]),
    +      ['default-markup', 'htm', 'html', 'mxml', 'xhtml', 'xml', 'xsl']);
    +  registerLangHandler(
    +      createSimpleLexer(
    +          [
    +           [PR_PLAIN,        /^[\s]+/, null, ' \t\r\n'],
    +           [PR_ATTRIB_VALUE, /^(?:\"[^\"]*\"?|\'[^\']*\'?)/, null, '\"\'']
    +           ],
    +          [
    +           [PR_TAG,          /^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],
    +           [PR_ATTRIB_NAME,  /^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],
    +           ['lang-uq.val',   /^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],
    +           [PR_PUNCTUATION,  /^[=<>\/]+/],
    +           ['lang-js',       /^on\w+\s*=\s*\"([^\"]+)\"/i],
    +           ['lang-js',       /^on\w+\s*=\s*\'([^\']+)\'/i],
    +           ['lang-js',       /^on\w+\s*=\s*([^\"\'>\s]+)/i],
    +           ['lang-css',      /^style\s*=\s*\"([^\"]+)\"/i],
    +           ['lang-css',      /^style\s*=\s*\'([^\']+)\'/i],
    +           ['lang-css',      /^style\s*=\s*([^\"\'>\s]+)/i]
    +           ]),
    +      ['in.tag']);
    +  registerLangHandler(
    +      createSimpleLexer([], [[PR_ATTRIB_VALUE, /^[\s\S]+/]]), ['uq.val']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': CPP_KEYWORDS,
    +          'hashComments': true,
    +          'cStyleComments': true,
    +          'types': C_TYPES
    +        }), ['c', 'cc', 'cpp', 'cxx', 'cyc', 'm']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': 'null,true,false'
    +        }), ['json']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': CSHARP_KEYWORDS,
    +          'hashComments': true,
    +          'cStyleComments': true,
    +          'verbatimStrings': true,
    +          'types': C_TYPES
    +        }), ['cs']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': JAVA_KEYWORDS,
    +          'cStyleComments': true
    +        }), ['java']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': SH_KEYWORDS,
    +          'hashComments': true,
    +          'multiLineStrings': true
    +        }), ['bash', 'bsh', 'csh', 'sh']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': PYTHON_KEYWORDS,
    +          'hashComments': true,
    +          'multiLineStrings': true,
    +          'tripleQuotedStrings': true
    +        }), ['cv', 'py', 'python']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': PERL_KEYWORDS,
    +          'hashComments': true,
    +          'multiLineStrings': true,
    +          'regexLiterals': 2  // multiline regex literals
    +        }), ['perl', 'pl', 'pm']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': RUBY_KEYWORDS,
    +          'hashComments': true,
    +          'multiLineStrings': true,
    +          'regexLiterals': true
    +        }), ['rb', 'ruby']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': JSCRIPT_KEYWORDS,
    +          'cStyleComments': true,
    +          'regexLiterals': true
    +        }), ['javascript', 'js']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': COFFEE_KEYWORDS,
    +          'hashComments': 3,  // ### style block comments
    +          'cStyleComments': true,
    +          'multilineStrings': true,
    +          'tripleQuotedStrings': true,
    +          'regexLiterals': true
    +        }), ['coffee']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': RUST_KEYWORDS,
    +          'cStyleComments': true,
    +          'multilineStrings': true
    +        }), ['rc', 'rs', 'rust']);
    +  registerLangHandler(
    +      createSimpleLexer([], [[PR_STRING, /^[\s\S]+/]]), ['regex']);
    +
    +  function applyDecorator(job) {
    +    var opt_langExtension = job.langExtension;
    +
    +    try {
    +      // Extract tags, and convert the source code to plain text.
    +      var sourceAndSpans = extractSourceSpans(job.sourceNode, job.pre);
    +      /** Plain text. @type {string} */
    +      var source = sourceAndSpans.sourceCode;
    +      job.sourceCode = source;
    +      job.spans = sourceAndSpans.spans;
    +      job.basePos = 0;
    +
    +      // Apply the appropriate language handler
    +      langHandlerForExtension(opt_langExtension, source)(job);
    +
    +      // Integrate the decorations and tags back into the source code,
    +      // modifying the sourceNode in place.
    +      recombineTagsAndDecorations(job);
    +    } catch (e) {
    +      if (win['console']) {
    +        console['log'](e && e['stack'] || e);
    +      }
    +    }
    +  }
    +
    +  /**
    +   * Pretty print a chunk of code.
    +   * @param sourceCodeHtml {string} The HTML to pretty print.
    +   * @param opt_langExtension {string} The language name to use.
    +   *     Typically, a filename extension like 'cpp' or 'java'.
    +   * @param opt_numberLines {number|boolean} True to number lines,
    +   *     or the 1-indexed number of the first line in sourceCodeHtml.
    +   */
    +  function $prettyPrintOne(sourceCodeHtml, opt_langExtension, opt_numberLines) {
    +    var container = document.createElement('div');
    +    // This could cause images to load and onload listeners to fire.
    +    // E.g. <img onerror="alert(1337)" src="nosuchimage.png">.
    +    // We assume that the inner HTML is from a trusted source.
    +    // The pre-tag is required for IE8 which strips newlines from innerHTML
    +    // when it is injected into a <pre> tag.
    +    // http://stackoverflow.com/questions/451486/pre-tag-loses-line-breaks-when-setting-innerhtml-in-ie
    +    // http://stackoverflow.com/questions/195363/inserting-a-newline-into-a-pre-tag-ie-javascript
    +    container.innerHTML = '<pre>' + sourceCodeHtml + '</pre>';
    +    container = container.firstChild;
    +    if (opt_numberLines) {
    +      numberLines(container, opt_numberLines, true);
    +    }
    +
    +    var job = {
    +      langExtension: opt_langExtension,
    +      numberLines: opt_numberLines,
    +      sourceNode: container,
    +      pre: 1
    +    };
    +    applyDecorator(job);
    +    return container.innerHTML;
    +  }
    +
    +   /**
    +    * Find all the {@code <pre>} and {@code <code>} tags in the DOM with
    +    * {@code class=prettyprint} and prettify them.
    +    *
    +    * @param {Function} opt_whenDone called when prettifying is done.
    +    * @param {HTMLElement|HTMLDocument} opt_root an element or document
    +    *   containing all the elements to pretty print.
    +    *   Defaults to {@code document.body}.
    +    */
    +  function $prettyPrint(opt_whenDone, opt_root) {
    +    var root = opt_root || document.body;
    +    var doc = root.ownerDocument || document;
    +    function byTagName(tn) { return root.getElementsByTagName(tn); }
    +    // fetch a list of nodes to rewrite
    +    var codeSegments = [byTagName('pre'), byTagName('code'), byTagName('xmp')];
    +    var elements = [];
    +    for (var i = 0; i < codeSegments.length; ++i) {
    +      for (var j = 0, n = codeSegments[i].length; j < n; ++j) {
    +        elements.push(codeSegments[i][j]);
    +      }
    +    }
    +    codeSegments = null;
    +
    +    var clock = Date;
    +    if (!clock['now']) {
    +      clock = { 'now': function () { return +(new Date); } };
    +    }
    +
    +    // The loop is broken into a series of continuations to make sure that we
    +    // don't make the browser unresponsive when rewriting a large page.
    +    var k = 0;
    +    var prettyPrintingJob;
    +
    +    var langExtensionRe = /\blang(?:uage)?-([\w.]+)(?!\S)/;
    +    var prettyPrintRe = /\bprettyprint\b/;
    +    var prettyPrintedRe = /\bprettyprinted\b/;
    +    var preformattedTagNameRe = /pre|xmp/i;
    +    var codeRe = /^code$/i;
    +    var preCodeXmpRe = /^(?:pre|code|xmp)$/i;
    +    var EMPTY = {};
    +
    +    function doWork() {
    +      var endTime = (win['PR_SHOULD_USE_CONTINUATION'] ?
    +                     clock['now']() + 250 /* ms */ :
    +                     Infinity);
    +      for (; k < elements.length && clock['now']() < endTime; k++) {
    +        var cs = elements[k];
    +
    +        // Look for a preceding comment like
    +        // <?prettify lang="..." linenums="..."?>
    +        var attrs = EMPTY;
    +        {
    +          for (var preceder = cs; (preceder = preceder.previousSibling);) {
    +            var nt = preceder.nodeType;
    +            // <?foo?> is parsed by HTML 5 to a comment node (8)
    +            // like <!--?foo?-->, but in XML is a processing instruction
    +            var value = (nt === 7 || nt === 8) && preceder.nodeValue;
    +            if (value
    +                ? !/^\??prettify\b/.test(value)
    +                : (nt !== 3 || /\S/.test(preceder.nodeValue))) {
    +              // Skip over white-space text nodes but not others.
    +              break;
    +            }
    +            if (value) {
    +              attrs = {};
    +              value.replace(
    +                  /\b(\w+)=([\w:.%+-]+)/g,
    +                function (_, name, value) { attrs[name] = value; });
    +              break;
    +            }
    +          }
    +        }
    +
    +        var className = cs.className;
    +        if ((attrs !== EMPTY || prettyPrintRe.test(className))
    +            // Don't redo this if we've already done it.
    +            // This allows recalling pretty print to just prettyprint elements
    +            // that have been added to the page since last call.
    +            && !prettyPrintedRe.test(className)) {
    +
    +          // make sure this is not nested in an already prettified element
    +          var nested = false;
    +          for (var p = cs.parentNode; p; p = p.parentNode) {
    +            var tn = p.tagName;
    +            if (preCodeXmpRe.test(tn)
    +                && p.className && prettyPrintRe.test(p.className)) {
    +              nested = true;
    +              break;
    +            }
    +          }
    +          if (!nested) {
    +            // Mark done.  If we fail to prettyprint for whatever reason,
    +            // we shouldn't try again.
    +            cs.className += ' prettyprinted';
    +
    +            // If the classes includes a language extensions, use it.
    +            // Language extensions can be specified like
    +            //     <pre class="prettyprint lang-cpp">
    +            // the language extension "cpp" is used to find a language handler
    +            // as passed to PR.registerLangHandler.
    +            // HTML5 recommends that a language be specified using "language-"
    +            // as the prefix instead.  Google Code Prettify supports both.
    +            // http://dev.w3.org/html5/spec-author-view/the-code-element.html
    +            var langExtension = attrs['lang'];
    +            if (!langExtension) {
    +              langExtension = className.match(langExtensionRe);
    +              // Support <pre class="prettyprint"><code class="language-c">
    +              var wrapper;
    +              if (!langExtension && (wrapper = childContentWrapper(cs))
    +                  && codeRe.test(wrapper.tagName)) {
    +                langExtension = wrapper.className.match(langExtensionRe);
    +              }
    +
    +              if (langExtension) { langExtension = langExtension[1]; }
    +            }
    +
    +            var preformatted;
    +            if (preformattedTagNameRe.test(cs.tagName)) {
    +              preformatted = 1;
    +            } else {
    +              var currentStyle = cs['currentStyle'];
    +              var defaultView = doc.defaultView;
    +              var whitespace = (
    +                  currentStyle
    +                  ? currentStyle['whiteSpace']
    +                  : (defaultView
    +                     && defaultView.getComputedStyle)
    +                  ? defaultView.getComputedStyle(cs, null)
    +                  .getPropertyValue('white-space')
    +                  : 0);
    +              preformatted = whitespace
    +                  && 'pre' === whitespace.substring(0, 3);
    +            }
    +
    +            // Look for a class like linenums or linenums:<n> where <n> is the
    +            // 1-indexed number of the first line.
    +            var lineNums = attrs['linenums'];
    +            if (!(lineNums = lineNums === 'true' || +lineNums)) {
    +              lineNums = className.match(/\blinenums\b(?::(\d+))?/);
    +              lineNums =
    +                lineNums
    +                ? lineNums[1] && lineNums[1].length
    +                  ? +lineNums[1] : true
    +                : false;
    +            }
    +            if (lineNums) { numberLines(cs, lineNums, preformatted); }
    +
    +            // do the pretty printing
    +            prettyPrintingJob = {
    +              langExtension: langExtension,
    +              sourceNode: cs,
    +              numberLines: lineNums,
    +              pre: preformatted
    +            };
    +            applyDecorator(prettyPrintingJob);
    +          }
    +        }
    +      }
    +      if (k < elements.length) {
    +        // finish up in a continuation
    +        setTimeout(doWork, 250);
    +      } else if ('function' === typeof opt_whenDone) {
    +        opt_whenDone();
    +      }
    +    }
    +
    +    doWork();
    +  }
    +
    +  /**
    +   * Contains functions for creating and registering new language handlers.
    +   * @type {Object}
    +   */
    +  var PR = win['PR'] = {
    +        'createSimpleLexer': createSimpleLexer,
    +        'registerLangHandler': registerLangHandler,
    +        'sourceDecorator': sourceDecorator,
    +        'PR_ATTRIB_NAME': PR_ATTRIB_NAME,
    +        'PR_ATTRIB_VALUE': PR_ATTRIB_VALUE,
    +        'PR_COMMENT': PR_COMMENT,
    +        'PR_DECLARATION': PR_DECLARATION,
    +        'PR_KEYWORD': PR_KEYWORD,
    +        'PR_LITERAL': PR_LITERAL,
    +        'PR_NOCODE': PR_NOCODE,
    +        'PR_PLAIN': PR_PLAIN,
    +        'PR_PUNCTUATION': PR_PUNCTUATION,
    +        'PR_SOURCE': PR_SOURCE,
    +        'PR_STRING': PR_STRING,
    +        'PR_TAG': PR_TAG,
    +        'PR_TYPE': PR_TYPE,
    +        'prettyPrintOne':
    +           IN_GLOBAL_SCOPE
    +             ? (win['prettyPrintOne'] = $prettyPrintOne)
    +             : (prettyPrintOne = $prettyPrintOne),
    +        'prettyPrint': prettyPrint =
    +           IN_GLOBAL_SCOPE
    +             ? (win['prettyPrint'] = $prettyPrint)
    +             : (prettyPrint = $prettyPrint)
    +      };
    +
    +  // Make PR available via the Asynchronous Module Definition (AMD) API.
    +  // Per https://github.com/amdjs/amdjs-api/wiki/AMD:
    +  // The Asynchronous Module Definition (AMD) API specifies a
    +  // mechanism for defining modules such that the module and its
    +  // dependencies can be asynchronously loaded.
    +  // ...
    +  // To allow a clear indicator that a global define function (as
    +  // needed for script src browser loading) conforms to the AMD API,
    +  // any global define function SHOULD have a property called "amd"
    +  // whose value is an object. This helps avoid conflict with any
    +  // other existing JavaScript code that could have defined a define()
    +  // function that does not conform to the AMD API.
    +  if (typeof define === "function" && define['amd']) {
    +    define("google-code-prettify", [], function () {
    +      return PR; 
    +    });
    +  }
    +})();